serai/coins/monero/src/transaction.rs

292 lines
7.4 KiB
Rust
Raw Normal View History

use core::cmp::Ordering;
use curve25519_dalek::edwards::EdwardsPoint;
2022-07-15 05:26:07 +00:00
use crate::{
hash,
serialize::*,
ringct::{RctPrunable, RctSignatures},
};
pub const RING_LEN: usize = 11;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Input {
Gen(u64),
2022-07-15 05:26:07 +00:00
ToKey { amount: u64, key_offsets: Vec<u64>, key_image: EdwardsPoint },
}
impl Input {
// Worst-case predictive len
pub(crate) fn fee_weight() -> usize {
// Uses 1 byte for the VarInt amount due to amount being 0
// Uses 1 byte for the VarInt encoding of the length of the ring as well
1 + 1 + 1 + (8 * RING_LEN) + 32
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
match self {
Input::Gen(height) => {
w.write_all(&[255])?;
write_varint(height, w)
2022-07-15 05:26:07 +00:00
}
Input::ToKey { amount, key_offsets, key_image } => {
w.write_all(&[2])?;
write_varint(amount, w)?;
write_vec(write_varint, key_offsets, w)?;
write_point(key_image, w)
}
}
}
2022-05-22 00:27:21 +00:00
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Input> {
let mut variant = [0];
2022-05-22 00:27:21 +00:00
r.read_exact(&mut variant)?;
2022-07-15 05:26:07 +00:00
Ok(match variant[0] {
255 => Input::Gen(read_varint(r)?),
2 => Input::ToKey {
amount: read_varint(r)?,
key_offsets: read_vec(read_varint, r)?,
key_image: read_point(r)?,
},
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Tried to deserialize unknown/unused input type",
))?,
})
2022-05-22 00:27:21 +00:00
}
}
// Doesn't bother moving to an enum for the unused Script classes
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Output {
pub amount: u64,
pub key: EdwardsPoint,
2022-07-15 05:26:07 +00:00
pub tag: Option<u8>,
}
impl Output {
pub(crate) fn fee_weight() -> usize {
1 + 1 + 32 + 1
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
write_varint(&self.amount, w)?;
w.write_all(&[2 + (if self.tag.is_some() { 1 } else { 0 })])?;
write_point(&self.key, w)?;
if let Some(tag) = self.tag {
w.write_all(&[tag])?;
}
Ok(())
}
2022-05-22 00:27:21 +00:00
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Output> {
let amount = read_varint(r)?;
let mut tag = [0];
2022-05-22 00:27:21 +00:00
r.read_exact(&mut tag)?;
if (tag[0] != 2) && (tag[0] != 3) {
2022-07-15 05:26:07 +00:00
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Tried to deserialize unknown/unused output type",
))?;
2022-05-22 00:27:21 +00:00
}
2022-07-15 05:26:07 +00:00
Ok(Output {
amount,
key: read_point(r)?,
tag: if tag[0] == 3 {
r.read_exact(&mut tag)?;
Some(tag[0])
} else {
None
},
})
2022-05-22 00:27:21 +00:00
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Timelock {
None,
Block(usize),
2022-07-15 05:26:07 +00:00
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)
}
}
pub(crate) fn fee_weight() -> usize {
8
}
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(),
2022-07-15 05:26:07 +00:00
Timelock::Time(time) => *time,
},
2022-07-15 05:26:07 +00:00
w,
)
}
}
impl PartialOrd for Timelock {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Timelock::None, _) => Some(Ordering::Less),
(Timelock::Block(a), Timelock::Block(b)) => a.partial_cmp(b),
(Timelock::Time(a), Timelock::Time(b)) => a.partial_cmp(b),
2022-07-15 05:26:07 +00:00
_ => None,
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TransactionPrefix {
pub version: u64,
pub timelock: Timelock,
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
2022-07-15 05:26:07 +00:00
pub extra: Vec<u8>,
}
impl TransactionPrefix {
pub(crate) fn fee_weight(inputs: usize, outputs: usize, extra: usize) -> usize {
// Assumes Timelock::None since this library won't let you create a TX with a timelock
1 + 1 +
2022-07-15 05:26:07 +00:00
varint_len(inputs) +
(inputs * Input::fee_weight()) +
1 +
(outputs * Output::fee_weight()) +
varint_len(extra) +
extra
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
write_varint(&self.version, 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)?;
w.write_all(&self.extra)
}
2022-05-22 00:27:21 +00:00
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<TransactionPrefix> {
let mut prefix = TransactionPrefix {
version: read_varint(r)?,
timelock: Timelock::from_raw(read_varint(r)?),
2022-05-22 00:27:21 +00:00
inputs: read_vec(Input::deserialize, r)?,
outputs: read_vec(Output::deserialize, r)?,
2022-07-15 05:26:07 +00:00
extra: vec![],
2022-05-22 00:27:21 +00:00
};
let len = read_varint(r)?;
prefix.extra.resize(len.try_into().unwrap(), 0);
r.read_exact(&mut prefix.extra)?;
Ok(prefix)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Transaction {
pub prefix: TransactionPrefix,
2022-07-15 05:26:07 +00:00
pub rct_signatures: RctSignatures,
}
impl Transaction {
pub(crate) fn fee_weight(inputs: usize, outputs: usize, extra: usize) -> usize {
2022-07-15 05:26:07 +00:00
TransactionPrefix::fee_weight(inputs, outputs, extra) +
RctSignatures::fee_weight(inputs, outputs)
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
self.prefix.serialize(w)?;
self.rct_signatures.serialize(w)
}
2022-05-22 00:27:21 +00:00
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Transaction> {
let prefix = TransactionPrefix::deserialize(r)?;
2022-07-15 05:26:07 +00:00
Ok(Transaction {
rct_signatures: RctSignatures::deserialize(
2022-05-22 00:27:21 +00:00
prefix
2022-07-15 05:26:07 +00:00
.inputs
.iter()
.map(|input| match input {
Input::Gen(_) => 0,
Input::ToKey { key_offsets, .. } => key_offsets.len(),
})
.collect(),
prefix.outputs.len(),
r,
)?,
prefix,
})
2022-05-22 00:27:21 +00:00
}
pub fn hash(&self) -> [u8; 32] {
let mut serialized = Vec::with_capacity(2048);
if self.prefix.version == 1 {
self.serialize(&mut serialized).unwrap();
hash(&serialized)
} else {
let mut sig_hash = Vec::with_capacity(96);
self.prefix.serialize(&mut serialized).unwrap();
sig_hash.extend(hash(&serialized));
serialized.clear();
2022-07-15 05:26:07 +00:00
self
.rct_signatures
.base
.serialize(&mut serialized, self.rct_signatures.prunable.rct_type())
.unwrap();
sig_hash.extend(hash(&serialized));
serialized.clear();
2022-05-22 03:16:06 +00:00
match self.rct_signatures.prunable {
RctPrunable::Null => serialized.resize(32, 0),
_ => {
self.rct_signatures.prunable.serialize(&mut serialized).unwrap();
serialized = hash(&serialized).to_vec();
}
}
sig_hash.extend(&serialized);
hash(&sig_hash)
}
}
pub fn signature_hash(&self) -> [u8; 32] {
let mut serialized = Vec::with_capacity(2048);
let mut sig_hash = Vec::with_capacity(96);
self.prefix.serialize(&mut serialized).unwrap();
sig_hash.extend(hash(&serialized));
serialized.clear();
2022-07-15 05:26:07 +00:00
self
.rct_signatures
.base
.serialize(&mut serialized, self.rct_signatures.prunable.rct_type())
.unwrap();
sig_hash.extend(hash(&serialized));
serialized.clear();
self.rct_signatures.prunable.signature_serialize(&mut serialized).unwrap();
sig_hash.extend(&hash(&serialized));
hash(&sig_hash)
}
}