mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
[WIP] More transaction stuff
This commit is contained in:
parent
36b8da0117
commit
21f31ccb8d
@ -11,7 +11,7 @@ use rand::rngs::OsRng;
|
||||
use std::convert::TryInto;
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
const RING_SIZE: usize = 11;
|
||||
pub const RING_SIZE: usize = 11;
|
||||
const DOMAIN_TAG: &str = "CSLAG_c";
|
||||
|
||||
fn challenge(
|
||||
@ -154,6 +154,24 @@ impl Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for monero::util::ringct::Clsag {
|
||||
fn from(from: Signature) -> Self {
|
||||
Self {
|
||||
s: from
|
||||
.responses
|
||||
.iter()
|
||||
.map(|s| monero::util::ringct::Key { key: s.to_bytes() })
|
||||
.collect(),
|
||||
c1: monero::util::ringct::Key {
|
||||
key: from.h_0.to_bytes(),
|
||||
},
|
||||
D: monero::util::ringct::Key {
|
||||
key: from.I.compress().to_bytes(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alice0 {
|
||||
// secret index is always 0
|
||||
ring: [EdwardsPoint; RING_SIZE],
|
||||
|
@ -1,9 +1,19 @@
|
||||
use monero_rpc::monerod;
|
||||
use rand::{SeedableRng, Rng};
|
||||
use monero_rpc::monerod::{MonerodRpc, GetOutputsOut};
|
||||
use monero::{Transaction, TransactionPrefix, VarInt, TxIn};
|
||||
use std::iter;
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use hash_edwards_to_edwards::hash_point_to_point;
|
||||
use itertools::Itertools;
|
||||
use monero::cryptonote::hash::Hashable;
|
||||
use monero::util::ringct::{RctSig, RctSigBase, RctSigPrunable, RctType};
|
||||
use monero::{Transaction, TransactionPrefix, TxIn, VarInt};
|
||||
use monero_rpc::monerod;
|
||||
use monero_rpc::monerod::{GetOutputsOut, MonerodRpc};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::convert::TryInto;
|
||||
use std::iter;
|
||||
|
||||
// [0u8; 32] = 466iKkx7MqVGD46dje3kwvSQRMfhNCvGaXTRATbQgz7kS8XTMmRmoTw9oJRRj523kTdQj8gXnF2xU9fmEPy9WXTr6pwetQj
|
||||
// [1u8; 32] = 47HCnKkBEeYfX5pScvBETAKdjBEPN7FcXEJPUqDPzWGCc6wC8VAdS8CjdtgKuSaY72K8fkoswjp176vbSPS8hzS17EZv8gj
|
||||
@ -13,19 +23,27 @@ async fn monerod_integration_test() {
|
||||
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 s_prime_a = curve25519_dalek::scalar::Scalar::random(&mut rng);
|
||||
let s_b = curve25519_dalek::scalar::Scalar::random(&mut rng);
|
||||
let lock_kp = monero::KeyPair {
|
||||
view: monero::PrivateKey::from_scalar(curve25519_dalek::scalar::Scalar::random(&mut rng)),
|
||||
spend: monero::PrivateKey::from_scalar(s_prime_a + s_b),
|
||||
};
|
||||
|
||||
let spend_tx = "d5d82405a655ebf721e32c8ef2f8b77f2476dd7cbdb06c05b9735abdf9a2a927".parse().unwrap();
|
||||
let lock_address = monero::Address::from_keypair(monero::Network::Mainnet, &lock_kp);
|
||||
|
||||
dbg!(lock_address);
|
||||
|
||||
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 (lower, upper) = (VarInt(77), VarInt(117));
|
||||
|
||||
let mut key_offsets = Vec::with_capacity(11);
|
||||
key_offsets.push(VarInt(real_key_offset));
|
||||
@ -42,15 +60,29 @@ async fn monerod_integration_test() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let response = client
|
||||
.get_outs(
|
||||
key_offsets
|
||||
.iter()
|
||||
.map(|offset| GetOutputsOut {
|
||||
amount: 0,
|
||||
index: offset.0,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let ring = response
|
||||
.outs
|
||||
.iter()
|
||||
.map(|out| out.key.point.decompress().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
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,
|
||||
@ -133,22 +165,54 @@ async fn monerod_integration_test() {
|
||||
|
||||
let relative_key_offsets = to_relative_offsets(&key_offsets);
|
||||
|
||||
let amount = 10_000_000;
|
||||
let fee = 10_000;
|
||||
// TODO: Pay lock amount (= amount + fee) to shared address (s_prime_a + s_b)
|
||||
|
||||
let (bulletproof, out_pk, _) = monero::make_bulletproof(&mut rng, &[amount]).unwrap();
|
||||
let out_pk = out_pk
|
||||
.iter()
|
||||
.map(|c| monero::util::ringct::CtKey {
|
||||
mask: monero::util::ringct::Key { key: c.to_bytes() },
|
||||
})
|
||||
.collect();
|
||||
|
||||
let 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(),
|
||||
};
|
||||
|
||||
let (adaptor_sig, adaptor) =
|
||||
single_party_adaptor_sig(s_prime_a, s_b, ring, &prefix.hash().to_bytes());
|
||||
|
||||
let sig = adaptor_sig.adapt(adaptor);
|
||||
|
||||
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()
|
||||
prefix,
|
||||
signatures: Vec::new(),
|
||||
rct_signatures: RctSig {
|
||||
sig: Some(RctSigBase {
|
||||
rct_type: RctType::Clsag,
|
||||
txn_fee: VarInt(fee),
|
||||
pseudo_outs: Vec::new(),
|
||||
ecdh_info: todo!(),
|
||||
out_pk,
|
||||
}),
|
||||
p: Some(RctSigPrunable {
|
||||
range_sigs: Vec::new(),
|
||||
bulletproofs: vec![bulletproof],
|
||||
MGs: Vec::new(),
|
||||
Clsags: vec![sig.into()],
|
||||
pseudo_outs: todo!(),
|
||||
}),
|
||||
},
|
||||
signatures: vec![],
|
||||
rct_signatures: Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
@ -156,10 +220,48 @@ 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));
|
||||
let diffs = vals
|
||||
.zip(next_vals)
|
||||
.map(|(cur, next)| VarInt(next.0 - cur.0));
|
||||
iter::once(offsets[0].clone()).chain(diffs).collect()
|
||||
}
|
||||
|
||||
/// First element of ring is the real pk.
|
||||
fn single_party_adaptor_sig(
|
||||
s_prime_a: Scalar,
|
||||
s_b: Scalar,
|
||||
ring: [EdwardsPoint; monero_adaptor::RING_SIZE],
|
||||
msg: &[u8; 32],
|
||||
) -> (monero_adaptor::AdaptorSignature, Scalar) {
|
||||
let (r_a, R_a, R_prime_a) = {
|
||||
let r_a = Scalar::random(&mut OsRng);
|
||||
let R_a = r_a * ED25519_BASEPOINT_POINT;
|
||||
|
||||
let pk_hashed_to_point = hash_point_to_point(ring[0]);
|
||||
|
||||
let R_prime_a = r_a * pk_hashed_to_point;
|
||||
|
||||
(r_a, R_a, R_prime_a)
|
||||
};
|
||||
|
||||
let alice = monero_adaptor::Alice0::new(ring, *msg, R_a, R_prime_a, s_prime_a).unwrap();
|
||||
let bob = monero_adaptor::Bob0::new(ring, *msg, R_a, R_prime_a, s_b).unwrap();
|
||||
|
||||
let msg = alice.next_message();
|
||||
let bob = bob.receive(msg);
|
||||
|
||||
let msg = bob.next_message();
|
||||
let alice = alice.receive(msg).unwrap();
|
||||
|
||||
let msg = alice.next_message();
|
||||
let bob = bob.receive(msg).unwrap();
|
||||
|
||||
let msg = bob.next_message();
|
||||
let alice = alice.receive(msg);
|
||||
|
||||
(alice.adaptor_sig, r_a)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -182,19 +284,21 @@ mod tests {
|
||||
|
||||
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),
|
||||
])
|
||||
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),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ impl Client {
|
||||
.inner
|
||||
.post(self.get_transactions.clone())
|
||||
.json(&GetTransactionsPayload {
|
||||
txs_hashes: txids.iter().map(|id| format!("{:x}", id)).collect()
|
||||
txs_hashes: txids.iter().map(|id| format!("{:x}", id)).collect(),
|
||||
})
|
||||
.send()
|
||||
.await?;
|
||||
@ -67,9 +67,10 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn get_o_indexes(&self, txid: Hash) -> Result<GetOIndexesResponse> {
|
||||
self.binary_request(self.get_o_indexes_bin_url.clone(), GetOIndexesPayload {
|
||||
txid,
|
||||
})
|
||||
self.binary_request(
|
||||
self.get_o_indexes_bin_url.clone(),
|
||||
GetOIndexesPayload { txid },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -106,7 +107,7 @@ pub struct GenerateBlocks {
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
pub struct BlockCount {
|
||||
pub count: u32,
|
||||
}
|
||||
@ -154,7 +155,7 @@ struct GetTransactionsResponse {
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct GetTransactionsResponseEntry {
|
||||
#[serde(with = "monero_serde_hex_transaction")]
|
||||
as_hex: Transaction
|
||||
as_hex: Transaction,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
@ -168,20 +169,20 @@ struct GetOutsPayload {
|
||||
outputs: Vec<GetOutputsOut>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, PartialEq)]
|
||||
pub struct GetOutputsOut {
|
||||
pub amount: u64,
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
pub struct GetOutsResponse {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseResponse,
|
||||
pub outs: Vec<OutKey>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
pub struct OutKey {
|
||||
pub height: u64,
|
||||
#[serde(with = "byte_array")]
|
||||
@ -193,7 +194,7 @@ pub struct OutKey {
|
||||
pub unlocked: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
pub struct BaseResponse {
|
||||
pub credits: u64,
|
||||
pub status: Status,
|
||||
@ -201,7 +202,7 @@ pub struct BaseResponse {
|
||||
pub untrusted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
pub struct GetOIndexesResponse {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseResponse,
|
||||
@ -209,7 +210,7 @@ pub struct GetOIndexesResponse {
|
||||
pub o_indexes: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq)]
|
||||
pub enum Status {
|
||||
#[serde(rename = "OK")]
|
||||
Ok,
|
||||
|
@ -82,7 +82,7 @@ pub struct GetAddress {
|
||||
pub address: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||
pub struct GetBalance {
|
||||
pub balance: u64,
|
||||
pub blocks_to_unlock: u32,
|
||||
@ -133,19 +133,19 @@ pub struct Transfer {
|
||||
pub unsigned_txset: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct BlockHeight {
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[serde(from = "CheckTxKeyResponse")]
|
||||
pub struct CheckTxKey {
|
||||
pub confirmations: u64,
|
||||
pub received: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
struct CheckTxKeyResponse {
|
||||
pub confirmations: u64,
|
||||
pub received: u64,
|
||||
@ -175,7 +175,7 @@ pub struct GenerateFromKeys {
|
||||
pub info: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
pub struct Refreshed {
|
||||
pub blocks_fetched: u32,
|
||||
pub received_money: bool,
|
||||
@ -191,7 +191,7 @@ pub struct SweepAll {
|
||||
weight_list: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Copy, Debug, Clone, Deserialize)]
|
||||
pub struct Version {
|
||||
pub version: u32,
|
||||
}
|
||||
@ -207,7 +207,7 @@ pub type WalletOpened = Empty;
|
||||
/// 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
|
||||
/// to all the "deserialization" to happily continue.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
||||
pub struct Empty {}
|
||||
|
||||
fn opt_key_from_blank<'de, D>(deserializer: D) -> Result<Option<monero::PrivateKey>, D::Error>
|
||||
|
Loading…
Reference in New Issue
Block a user