mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
WIP SIGN STUFF
This commit is contained in:
parent
f3f1d96431
commit
47a63f3467
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2291,6 +2291,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"hash_edwards_to_edwards",
|
"hash_edwards_to_edwards",
|
||||||
|
"itertools 0.10.0",
|
||||||
"monero",
|
"monero",
|
||||||
"monero-harness",
|
"monero-harness",
|
||||||
"monero-rpc",
|
"monero-rpc",
|
||||||
|
@ -17,3 +17,4 @@ monero-harness = { path = "../monero-harness" }
|
|||||||
monero-rpc = { path = "../monero-rpc" }
|
monero-rpc = { path = "../monero-rpc" }
|
||||||
monero-wallet = { path = "../monero-wallet" }
|
monero-wallet = { path = "../monero-wallet" }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
itertools = "0.10"
|
||||||
|
@ -1,4 +1,200 @@
|
|||||||
|
use monero_rpc::monerod;
|
||||||
|
use rand::{SeedableRng, Rng};
|
||||||
|
use monero_rpc::monerod::{MonerodRpc, GetOutputsOut};
|
||||||
|
use monero::{Transaction, TransactionPrefix, VarInt, TxIn};
|
||||||
|
use std::iter;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
// [0u8; 32] = 466iKkx7MqVGD46dje3kwvSQRMfhNCvGaXTRATbQgz7kS8XTMmRmoTw9oJRRj523kTdQj8gXnF2xU9fmEPy9WXTr6pwetQj
|
||||||
|
// [1u8; 32] = 47HCnKkBEeYfX5pScvBETAKdjBEPN7FcXEJPUqDPzWGCc6wC8VAdS8CjdtgKuSaY72K8fkoswjp176vbSPS8hzS17EZv8gj
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn monerod_integration_test() {
|
async fn monerod_integration_test() {
|
||||||
let _client = monero_rpc::monerod::Client::localhost(18081).unwrap();
|
let client = monerod::Client::localhost(18081).unwrap();
|
||||||
|
let mut rng = rand::rngs::StdRng::from_seed([0u8; 32]);
|
||||||
|
|
||||||
|
let wallet = monero_wallet::Wallet::new_random(client.clone(), &mut rng);
|
||||||
|
|
||||||
|
let spend_tx = "d5d82405a655ebf721e32c8ef2f8b77f2476dd7cbdb06c05b9735abdf9a2a927".parse().unwrap();
|
||||||
|
|
||||||
|
let mut o_indexes_response = client.get_o_indexes(spend_tx).await.unwrap();
|
||||||
|
|
||||||
|
let real_key_offset = o_indexes_response.o_indexes.pop().unwrap();
|
||||||
|
|
||||||
|
// let (lower, upper) = dbg!(wallet.calculate_key_offset_boundaries().await.unwrap());
|
||||||
|
let (lower, upper) = (
|
||||||
|
VarInt(77),
|
||||||
|
VarInt(117),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut key_offsets = Vec::with_capacity(11);
|
||||||
|
key_offsets.push(VarInt(real_key_offset));
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
loop {
|
||||||
|
let decoy_offset = VarInt(rng.gen_range(lower.0, upper.0));
|
||||||
|
|
||||||
|
if key_offsets.contains(&decoy_offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_offsets.push(decoy_offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key_offsets.sort();
|
||||||
|
|
||||||
|
let outs = client.get_outs(key_offsets.iter().map(|offset| GetOutputsOut {
|
||||||
|
amount: 0,
|
||||||
|
index: offset.0
|
||||||
|
}).collect()).await.unwrap();
|
||||||
|
|
||||||
|
dbg!(outs);
|
||||||
|
|
||||||
|
// [
|
||||||
|
// OutKey {
|
||||||
|
// height: 80,
|
||||||
|
// key: 90160faa3f57077ee05eaae797d95eae96d6d6b31f02df274ded85a63b5f2987,
|
||||||
|
// mask: be8aa4ab10aab1cd1920b83243ebdfb84c2275dc0eaeee8b7e202c3d1f314c92,
|
||||||
|
// txid: 0x1062a5b667023ebbb39fbc8898ea43488f83fbf877c790895c3882d4f1ef65cd,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 85,
|
||||||
|
// key: 97d00aca5023c8092b1070442169b1088f533904e2938ad52696324ee1fbb780,
|
||||||
|
// mask: 1b68a61d530c63749269ee5b4b4ff251944489aa636e92b78dac78a5b0f964c7,
|
||||||
|
// txid: 0x505d33dd8e5e891e5e10010a8188d3f9303d1939b3801a3385e6acd15b81ab57,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 89,
|
||||||
|
// key: 3645d682a5a13e4f58fb3d711f247237251ba8f38d2bf6f00dc974db14e1ceac,
|
||||||
|
// mask: 2e3e47603f364de65dfce86090f9943a0f702f53cdd2fc03ab537c899a5eae67,
|
||||||
|
// txid: 0x96e730e7aebfbf9863f18541c12ab49fb6b9dd44eff85369b0b70f85a4afc37b,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 90,
|
||||||
|
// key: 5e0f69d43987c49d5ded18aa20ea81df9aa133a30e917ec7549e2f29978674ec,
|
||||||
|
// mask: d9f4bf0287eefb653575b1c7ed3a377f45c29318fbb5f91989ffa9c986acbeff,
|
||||||
|
// txid: 0x4855aa4b17e847b928fb452a762302962b80905ea83708f83a87d18884dc3adc,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 91,
|
||||||
|
// key: f3d362c830e20a365e6f2f13e05d21dabcb6ac9517351dcaa66da64d38af8a28,
|
||||||
|
// mask: c8e768cbf939f780f6197b8c0d7a1baa3caf0407fdee3f555fd791a740e85e6a,
|
||||||
|
// txid: 0x495d961a96db30d0bcbc89acda693ceb371ecf56b4b036f8c0511b5bf5688579,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 96,
|
||||||
|
// key: 1f51a5f61d7d8e2ae431488c62e4e6523c02856a03af7d0c51929fa9c94da0b8,
|
||||||
|
// mask: 167200b570a7b31bf23b3f8c111b03d073f128895a33a9de8ed2b8b839b42ff9,
|
||||||
|
// txid: 0xdb7ee874506e2f75d52e2ae7f4d140dbd37cceba870aef2ffe25a417094991d4,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 105,
|
||||||
|
// key: c34378e67680bd42964457294a7587ab8c4fa813c172f16faebd6f151db39369,
|
||||||
|
// mask: f034b8ff2cbd2729a7c19c0cc59a053fcbe1123f10acb0ec9e86bf122f0d3b12,
|
||||||
|
// txid: 0x5e2ca0b269c56e370cb8b243f4a7c5656195471dcb643a99036050fbb212f248,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 108,
|
||||||
|
// key: f8cf697bb285cbd61586543b2cd9e70f6b030348586d6e24c2a82cd8a4b4c8db,
|
||||||
|
// mask: 9ad72a2f1867a99367f045a512b7474234e0e08e39ba4f8fbd961d076348ee28,
|
||||||
|
// txid: 0x9a27def957d31482922cfd98a8422e92eba2a40f5ea1c285b5ac708b359f1a8d,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 109,
|
||||||
|
// key: 5b90003c16b591249d968cf401cd5ea99392963b401476746bad28606362bd42,
|
||||||
|
// mask: 69a62e221e990bc91453dfaee01dfc307dff680a6b270944598014df32097563,
|
||||||
|
// txid: 0xfc6b1a98cadbf19e84a0dccff53c34dbd4cf1e27c3558c913f7b3512d57d42b8,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 113,
|
||||||
|
// key: ad46eef99661c1e93ba517d3e541eebf9c42ba015566ff5dcc3a689edaf8f2ac,
|
||||||
|
// mask: b3012a4a5c0b3b465246e9671b7cdb84f817bedcc1ab9015aeff1f6ccffebb0c,
|
||||||
|
// txid: 0xf8a557dd14b6714eab5661b35944c4b09d0f8a9ef82b31ffb19244f7dfd127da,
|
||||||
|
// unlocked: false,
|
||||||
|
// },
|
||||||
|
// OutKey {
|
||||||
|
// height: 116,
|
||||||
|
// key: 76f445a4ff9a957d979f910040938a25b01759c7aad3605193502b35cdc8ff58,
|
||||||
|
// mask: a2091a955e819696b4526ae54f7dcf69f9fe7a42a0edcdb69f787437fc73244d,
|
||||||
|
// txid: 0xd5d82405a655ebf721e32c8ef2f8b77f2476dd7cbdb06c05b9735abdf9a2a927,
|
||||||
|
// unlocked: true,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
|
||||||
|
let relative_key_offsets = to_relative_offsets(&key_offsets);
|
||||||
|
|
||||||
|
let transaction = Transaction {
|
||||||
|
prefix: TransactionPrefix {
|
||||||
|
version: VarInt(2),
|
||||||
|
unlock_time: Default::default(),
|
||||||
|
inputs: vec![
|
||||||
|
TxIn::ToKey {
|
||||||
|
amount: VarInt(0),
|
||||||
|
key_offsets: relative_key_offsets,
|
||||||
|
k_image: todo!()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
outputs: vec![], // 498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj
|
||||||
|
extra: Default::default()
|
||||||
|
},
|
||||||
|
signatures: vec![],
|
||||||
|
rct_signatures: Default::default()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_relative_offsets(offsets: &[VarInt]) -> Vec<VarInt> {
|
||||||
|
let vals = offsets.iter();
|
||||||
|
let next_vals = offsets.iter().skip(1);
|
||||||
|
|
||||||
|
let diffs = vals.zip(next_vals).map(|(cur, next)| VarInt(next.0 - cur.0));
|
||||||
|
iter::once(offsets[0].clone()).chain(diffs).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculate_relative_key_offsets() {
|
||||||
|
let key_offsets = [
|
||||||
|
VarInt(78),
|
||||||
|
VarInt(81),
|
||||||
|
VarInt(91),
|
||||||
|
VarInt(91),
|
||||||
|
VarInt(96),
|
||||||
|
VarInt(98),
|
||||||
|
VarInt(101),
|
||||||
|
VarInt(112),
|
||||||
|
VarInt(113),
|
||||||
|
VarInt(114),
|
||||||
|
VarInt(117),
|
||||||
|
];
|
||||||
|
|
||||||
|
let relative_offsets = to_relative_offsets(&key_offsets);
|
||||||
|
|
||||||
|
assert_eq!(&relative_offsets, &[
|
||||||
|
VarInt(78),
|
||||||
|
VarInt(3),
|
||||||
|
VarInt(10),
|
||||||
|
VarInt(0),
|
||||||
|
VarInt(5),
|
||||||
|
VarInt(2),
|
||||||
|
VarInt(3),
|
||||||
|
VarInt(11),
|
||||||
|
VarInt(1),
|
||||||
|
VarInt(1),
|
||||||
|
VarInt(3),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use monero::{cryptonote::hash::Hash, util::ringct, PublicKey};
|
use monero::{cryptonote::hash::Hash, util::ringct, PublicKey, Transaction};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
#[jsonrpc_client::api(version = "2.0")]
|
#[jsonrpc_client::api(version = "2.0")]
|
||||||
@ -18,6 +18,7 @@ pub struct Client {
|
|||||||
base_url: reqwest::Url,
|
base_url: reqwest::Url,
|
||||||
get_o_indexes_bin_url: reqwest::Url,
|
get_o_indexes_bin_url: reqwest::Url,
|
||||||
get_outs_bin_url: reqwest::Url,
|
get_outs_bin_url: reqwest::Url,
|
||||||
|
get_transactions: reqwest::Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@ -40,9 +41,31 @@ impl Client {
|
|||||||
get_outs_bin_url: format!("http://{}:{}/get_outs.bin", host, port)
|
get_outs_bin_url: format!("http://{}:{}/get_outs.bin", host, port)
|
||||||
.parse()
|
.parse()
|
||||||
.context("url is well formed")?,
|
.context("url is well formed")?,
|
||||||
|
get_transactions: format!("http://{}:{}/get_transactions", host, port)
|
||||||
|
.parse()
|
||||||
|
.context("url is well formed")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_transactions(&self, txids: &[Hash]) -> Result<Vec<Transaction>> {
|
||||||
|
let response = self
|
||||||
|
.inner
|
||||||
|
.post(self.get_transactions.clone())
|
||||||
|
.json(&GetTransactionsPayload {
|
||||||
|
txs_hashes: txids.iter().map(|id| format!("{:x}", id)).collect()
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
anyhow::bail!("Request failed with status code {}", response.status())
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = response.json::<GetTransactionsResponse>().await?;
|
||||||
|
|
||||||
|
Ok(response.txs.into_iter().map(|e| e.as_hex).collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_o_indexes(&self, txid: Hash) -> Result<GetOIndexesResponse> {
|
pub async fn get_o_indexes(&self, txid: Hash) -> Result<GetOIndexesResponse> {
|
||||||
self.binary_request(self.get_o_indexes_bin_url.clone(), GetOIndexesPayload {
|
self.binary_request(self.get_o_indexes_bin_url.clone(), GetOIndexesPayload {
|
||||||
txid,
|
txid,
|
||||||
@ -83,7 +106,7 @@ pub struct GenerateBlocks {
|
|||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct BlockCount {
|
pub struct BlockCount {
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
}
|
}
|
||||||
@ -118,6 +141,22 @@ pub struct GetIndexesResponse {
|
|||||||
pub o_indexes: Vec<u32>,
|
pub o_indexes: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
struct GetTransactionsPayload {
|
||||||
|
txs_hashes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
struct GetTransactionsResponse {
|
||||||
|
txs: Vec<GetTransactionsResponseEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
struct GetTransactionsResponseEntry {
|
||||||
|
#[serde(with = "monero_serde_hex_transaction")]
|
||||||
|
as_hex: Transaction
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
struct GetOIndexesPayload {
|
struct GetOIndexesPayload {
|
||||||
#[serde(with = "byte_array")]
|
#[serde(with = "byte_array")]
|
||||||
@ -129,20 +168,20 @@ struct GetOutsPayload {
|
|||||||
outputs: Vec<GetOutputsOut>,
|
outputs: Vec<GetOutputsOut>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct GetOutputsOut {
|
pub struct GetOutputsOut {
|
||||||
pub amount: u64,
|
pub amount: u64,
|
||||||
pub index: u64,
|
pub index: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct GetOutsResponse {
|
pub struct GetOutsResponse {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: BaseResponse,
|
pub base: BaseResponse,
|
||||||
pub outs: Vec<OutKey>,
|
pub outs: Vec<OutKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct OutKey {
|
pub struct OutKey {
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
#[serde(with = "byte_array")]
|
#[serde(with = "byte_array")]
|
||||||
@ -154,7 +193,7 @@ pub struct OutKey {
|
|||||||
pub unlocked: bool,
|
pub unlocked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct BaseResponse {
|
pub struct BaseResponse {
|
||||||
pub credits: u64,
|
pub credits: u64,
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
@ -162,7 +201,7 @@ pub struct BaseResponse {
|
|||||||
pub untrusted: bool,
|
pub untrusted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct GetOIndexesResponse {
|
pub struct GetOIndexesResponse {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: BaseResponse,
|
pub base: BaseResponse,
|
||||||
@ -170,7 +209,7 @@ pub struct GetOIndexesResponse {
|
|||||||
pub o_indexes: Vec<u64>,
|
pub o_indexes: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
#[serde(rename = "OK")]
|
#[serde(rename = "OK")]
|
||||||
Ok,
|
Ok,
|
||||||
@ -199,6 +238,27 @@ mod monero_serde_hex_block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod monero_serde_hex_transaction {
|
||||||
|
use super::*;
|
||||||
|
use monero::consensus::Decodable;
|
||||||
|
use serde::{de::Error, Deserialize, Deserializer};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<monero::Transaction, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let hex = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let bytes = hex::decode(&hex).map_err(D::Error::custom)?;
|
||||||
|
let mut cursor = Cursor::new(bytes);
|
||||||
|
|
||||||
|
let block = monero::Transaction::consensus_decode(&mut cursor).map_err(D::Error::custom)?;
|
||||||
|
|
||||||
|
Ok(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod byte_array {
|
mod byte_array {
|
||||||
use super::*;
|
use super::*;
|
||||||
use serde::{de::Error, Deserializer};
|
use serde::{de::Error, Deserializer};
|
||||||
|
@ -82,7 +82,7 @@ pub struct GetAddress {
|
|||||||
pub address: String,
|
pub address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct GetBalance {
|
pub struct GetBalance {
|
||||||
pub balance: u64,
|
pub balance: u64,
|
||||||
pub blocks_to_unlock: u32,
|
pub blocks_to_unlock: u32,
|
||||||
@ -133,19 +133,19 @@ pub struct Transfer {
|
|||||||
pub unsigned_txset: String,
|
pub unsigned_txset: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct BlockHeight {
|
pub struct BlockHeight {
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(from = "CheckTxKeyResponse")]
|
#[serde(from = "CheckTxKeyResponse")]
|
||||||
pub struct CheckTxKey {
|
pub struct CheckTxKey {
|
||||||
pub confirmations: u64,
|
pub confirmations: u64,
|
||||||
pub received: u64,
|
pub received: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
struct CheckTxKeyResponse {
|
struct CheckTxKeyResponse {
|
||||||
pub confirmations: u64,
|
pub confirmations: u64,
|
||||||
pub received: u64,
|
pub received: u64,
|
||||||
@ -175,7 +175,7 @@ pub struct GenerateFromKeys {
|
|||||||
pub info: String,
|
pub info: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct Refreshed {
|
pub struct Refreshed {
|
||||||
pub blocks_fetched: u32,
|
pub blocks_fetched: u32,
|
||||||
pub received_money: bool,
|
pub received_money: bool,
|
||||||
@ -191,7 +191,7 @@ pub struct SweepAll {
|
|||||||
weight_list: Vec<u32>,
|
weight_list: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
}
|
}
|
||||||
@ -207,7 +207,7 @@ pub type WalletOpened = Empty;
|
|||||||
/// what the response of every RPC call is. Unfortunately, monerod likes to
|
/// what the response of every RPC call is. Unfortunately, monerod likes to
|
||||||
/// return empty objects instead of `null`s in certain cases. We use this struct
|
/// return empty objects instead of `null`s in certain cases. We use this struct
|
||||||
/// to all the "deserialization" to happily continue.
|
/// to all the "deserialization" to happily continue.
|
||||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Empty {}
|
pub struct Empty {}
|
||||||
|
|
||||||
fn opt_key_from_blank<'de, D>(deserializer: D) -> Result<Option<monero::PrivateKey>, D::Error>
|
fn opt_key_from_blank<'de, D>(deserializer: D) -> Result<Option<monero::PrivateKey>, D::Error>
|
||||||
|
@ -9,9 +9,9 @@ anyhow = "1"
|
|||||||
monero = "0.12"
|
monero = "0.12"
|
||||||
monero-rpc = { path = "../monero-rpc" }
|
monero-rpc = { path = "../monero-rpc" }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
curve25519-dalek = "3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
curve25519-dalek = "3"
|
|
||||||
monero-harness = { path = "../monero-harness" }
|
monero-harness = { path = "../monero-harness" }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
testcontainers = "0.12"
|
testcontainers = "0.12"
|
||||||
|
@ -3,20 +3,35 @@ use monero::consensus::encode::VarInt;
|
|||||||
use monero::cryptonote::hash::Hashable;
|
use monero::cryptonote::hash::Hashable;
|
||||||
use monero_rpc::monerod;
|
use monero_rpc::monerod;
|
||||||
use monero_rpc::monerod::{GetBlockResponse, MonerodRpc as _};
|
use monero_rpc::monerod::{GetBlockResponse, MonerodRpc as _};
|
||||||
use rand::Rng;
|
use rand::{Rng, RngCore, CryptoRng};
|
||||||
|
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
client: monerod::Client,
|
client: monerod::Client,
|
||||||
|
key: monero::KeyPair
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
pub fn new_random<R: CryptoRng + RngCore>(client: monerod::Client, rng: &mut R) -> Self {
|
||||||
|
Self {
|
||||||
|
client,
|
||||||
|
key: monero::KeyPair {
|
||||||
|
view: monero::PrivateKey::from_scalar(curve25519_dalek::scalar::Scalar::random(rng)),
|
||||||
|
spend: monero::PrivateKey::from_scalar(curve25519_dalek::scalar::Scalar::random(rng)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_address(&self, network: monero::Network) -> monero::Address {
|
||||||
|
monero::Address::from_keypair(network, &self.key)
|
||||||
|
}
|
||||||
|
|
||||||
/// Chooses 10 random key offsets for use within a new confidential
|
/// Chooses 10 random key offsets for use within a new confidential
|
||||||
/// transactions.
|
/// transactions.
|
||||||
///
|
///
|
||||||
/// Choosing these offsets randomly is not ideal for privacy, instead they
|
/// 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
|
/// should be chosen in a way that mimics a real spending pattern as much as
|
||||||
/// possible.
|
/// possible.
|
||||||
pub async fn choose_ten_random_key_offsets(&self) -> Result<[VarInt; 10]> {
|
pub async fn calculate_key_offset_boundaries(&self) -> Result<(VarInt, VarInt)> {
|
||||||
let latest_block = self.client.get_block_count().await?;
|
let latest_block = self.client.get_block_count().await?;
|
||||||
let latest_spendable_block = latest_block.count - 10;
|
let latest_spendable_block = latest_block.count - 10;
|
||||||
|
|
||||||
@ -38,20 +53,7 @@ impl Wallet {
|
|||||||
.context("Expected at least one output index")?;
|
.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 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(oldest_index), VarInt(last_index)))
|
||||||
|
|
||||||
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)),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,23 +73,26 @@ mod tests {
|
|||||||
rpc_client.generateblocks(150, "498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj".to_owned()).await.unwrap();
|
rpc_client.generateblocks(150, "498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj".to_owned()).await.unwrap();
|
||||||
let wallet = Wallet {
|
let wallet = Wallet {
|
||||||
client: rpc_client.clone(),
|
client: rpc_client.clone(),
|
||||||
|
key: todo!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let key_offsets = wallet.choose_ten_random_key_offsets().await.unwrap();
|
let (lower, upper) = wallet.calculate_key_offset_boundaries().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);
|
todo!("fix");
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2021-04-25"
|
channel = "nightly-2021-05-04"
|
||||||
components = ["rustfmt", "clippy"]
|
components = ["rustfmt", "clippy"]
|
||||||
targets = ["armv7-unknown-linux-gnueabihf"]
|
targets = ["armv7-unknown-linux-gnueabihf"]
|
||||||
|
Loading…
Reference in New Issue
Block a user