mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-03 17:40:34 +00:00
Implement a proper Monero Timelock type
Transaction scanning now returns the timelock to ensure it's acknowledged by wallets. Fixes https://github.com/serai-dex/serai/issues/16.
This commit is contained in:
parent
2ae715f899
commit
dfd2f624ee
6 changed files with 62 additions and 23 deletions
|
@ -9,7 +9,7 @@ use serde_json::json;
|
|||
|
||||
use reqwest;
|
||||
|
||||
use crate::{transaction::{Input, Transaction}, block::Block};
|
||||
use crate::{transaction::{Input, Timelock, Transaction}, block::Block};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EmptyResponse {}
|
||||
|
@ -267,9 +267,12 @@ impl Rpc {
|
|||
// get the median time for the given height, yet we do need to in order to be complete
|
||||
outs.outs.iter().enumerate().map(
|
||||
|(i, out)| Ok(
|
||||
if txs[i].prefix.unlock_time <= u64::try_from(height).unwrap() {
|
||||
Some([rpc_point(&out.key)?, rpc_point(&out.mask)?])
|
||||
} else { None }
|
||||
Some([rpc_point(&out.key)?, rpc_point(&out.mask)?]).filter(|_| {
|
||||
match txs[i].prefix.timelock {
|
||||
Timelock::Block(t_height) => (t_height <= height),
|
||||
_ => false
|
||||
}
|
||||
})
|
||||
)
|
||||
).collect()
|
||||
}
|
||||
|
|
|
@ -84,10 +84,40 @@ impl Output {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum Timelock {
|
||||
None,
|
||||
Block(usize),
|
||||
Time(u64)
|
||||
}
|
||||
|
||||
impl Timelock {
|
||||
fn from_raw(raw: u64) -> Timelock {
|
||||
if raw == 0 {
|
||||
Timelock::None
|
||||
} else if raw < 500_000_000 {
|
||||
Timelock::Block(usize::try_from(raw).unwrap())
|
||||
} else {
|
||||
Timelock::Time(raw)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
write_varint(
|
||||
&match self {
|
||||
Timelock::None => 0,
|
||||
Timelock::Block(block) => (*block).try_into().unwrap(),
|
||||
Timelock::Time(time) => *time
|
||||
},
|
||||
w
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct TransactionPrefix {
|
||||
pub version: u64,
|
||||
pub unlock_time: u64,
|
||||
pub timelock: Timelock,
|
||||
pub inputs: Vec<Input>,
|
||||
pub outputs: Vec<Output>,
|
||||
pub extra: Vec<u8>
|
||||
|
@ -96,7 +126,7 @@ pub struct TransactionPrefix {
|
|||
impl TransactionPrefix {
|
||||
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
write_varint(&self.version, w)?;
|
||||
write_varint(&self.unlock_time, w)?;
|
||||
self.timelock.serialize(w)?;
|
||||
write_vec(Input::serialize, &self.inputs, w)?;
|
||||
write_vec(Output::serialize, &self.outputs, w)?;
|
||||
write_varint(&self.extra.len().try_into().unwrap(), w)?;
|
||||
|
@ -106,7 +136,7 @@ impl TransactionPrefix {
|
|||
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<TransactionPrefix> {
|
||||
let mut prefix = TransactionPrefix {
|
||||
version: read_varint(r)?,
|
||||
unlock_time: read_varint(r)?,
|
||||
timelock: Timelock::from_raw(read_varint(r)?),
|
||||
inputs: read_vec(Input::deserialize, r)?,
|
||||
outputs: read_vec(Output::deserialize, r)?,
|
||||
extra: vec![]
|
||||
|
|
|
@ -11,7 +11,7 @@ use monero::{consensus::deserialize, blockdata::transaction::ExtraField};
|
|||
use crate::{
|
||||
Commitment,
|
||||
serialize::{write_varint, read_32, read_scalar, read_point},
|
||||
transaction::Transaction,
|
||||
transaction::{Timelock, Transaction},
|
||||
wallet::{uniqueness, shared_key, amount_decryption, commitment_mask}
|
||||
};
|
||||
|
||||
|
@ -57,13 +57,7 @@ impl Transaction {
|
|||
&self,
|
||||
view: Scalar,
|
||||
spend: EdwardsPoint
|
||||
) -> Vec<SpendableOutput> {
|
||||
// Ignore transactions which utilize a timelock. Almost no transactions on Monero do,
|
||||
// and they're not worth the effort to track given their complexities
|
||||
if self.prefix.unlock_time != 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
) -> (Vec<SpendableOutput>, Timelock) {
|
||||
let mut extra = vec![];
|
||||
write_varint(&u64::try_from(self.prefix.extra.len()).unwrap(), &mut extra).unwrap();
|
||||
extra.extend(&self.prefix.extra);
|
||||
|
@ -81,7 +75,7 @@ impl Transaction {
|
|||
|
||||
pubkeys = m_pubkeys.iter().map(|key| key.point.decompress()).filter_map(|key| key).collect();
|
||||
} else {
|
||||
return vec![];
|
||||
return (vec![], self.prefix.timelock);
|
||||
};
|
||||
|
||||
let mut res = vec![];
|
||||
|
@ -136,6 +130,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
|
||||
(res, self.prefix.timelock)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
|||
bulletproofs::Bulletproofs,
|
||||
RctBase, RctPrunable, RctSignatures
|
||||
},
|
||||
transaction::{Input, Output, TransactionPrefix, Transaction},
|
||||
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
||||
rpc::{Rpc, RpcError},
|
||||
wallet::{SpendableOutput, Decoys, key_image_sort, uniqueness, shared_key, commitment_mask, amount_encryption}
|
||||
};
|
||||
|
@ -255,7 +255,7 @@ impl SignableTransaction {
|
|||
Transaction {
|
||||
prefix: TransactionPrefix {
|
||||
version: 2,
|
||||
unlock_time: 0,
|
||||
timelock: Timelock::None,
|
||||
inputs: vec![],
|
||||
outputs: tx_outputs,
|
||||
extra
|
||||
|
|
|
@ -100,7 +100,7 @@ async fn send_core(test: usize, multisig: bool) {
|
|||
|
||||
// Grab the largest output available
|
||||
let output = {
|
||||
let mut outputs = tx.as_ref().unwrap().scan(view, spend_pub);
|
||||
let mut outputs = tx.as_ref().unwrap().scan(view, spend_pub).0;
|
||||
outputs.sort_by(|x, y| x.commitment.amount.cmp(&y.commitment.amount).reverse());
|
||||
outputs.swap_remove(0)
|
||||
};
|
||||
|
@ -125,7 +125,7 @@ async fn send_core(test: usize, multisig: bool) {
|
|||
|
||||
for i in (start + 1) .. (start + 9) {
|
||||
let tx = rpc.get_block_transactions(i).await.unwrap().swap_remove(0);
|
||||
let output = tx.scan(view, spend_pub).swap_remove(0);
|
||||
let output = tx.scan(view, spend_pub).0.swap_remove(0);
|
||||
amount += output.commitment.amount;
|
||||
outputs.push(output);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use frost::MultisigKeys;
|
|||
use monero::util::address::Address;
|
||||
use monero_serai::{
|
||||
frost::Ed25519,
|
||||
transaction::Transaction,
|
||||
transaction::{Timelock, Transaction},
|
||||
rpc::Rpc,
|
||||
wallet::{SpendableOutput, SignableTransaction}
|
||||
};
|
||||
|
@ -90,7 +90,18 @@ impl Coin for Monero {
|
|||
}
|
||||
|
||||
async fn get_outputs(&self, block: &Self::Block, key: dfg::EdwardsPoint) -> Vec<Self::Output> {
|
||||
block.iter().flat_map(|tx| tx.scan(self.view, key.0)).map(Output::from).collect()
|
||||
block
|
||||
.iter()
|
||||
.flat_map(|tx| {
|
||||
let (outputs, timelock) = tx.scan(self.view, key.0);
|
||||
if timelock == Timelock::None {
|
||||
outputs
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.map(Output::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn prepare_send<R: RngCore + CryptoRng>(
|
||||
|
|
Loading…
Reference in a new issue