use super::ser::{EpeeRead, EpeeWrite, Reader, write_vec};
use super::types::Value256;
use super::variant::{Variant, tag};
use super::varint::append_varint;
use crate::crypto::Point;
use crate::error::Result;
pub const TRANSACTION_VERSION_INITIAL: u64 = 0;
pub const TRANSACTION_VERSION_PRE_HF4: u64 = 1;
pub const TRANSACTION_VERSION_POST_HF4: u64 = 2;
pub const TRANSACTION_VERSION_POST_HF5: u64 = 3;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TxInGen {
pub height: u64,
}
impl EpeeWrite for TxInGen {
fn write_epee(&self, out: &mut Vec<u8>) {
append_varint(out, self.height);
}
}
impl EpeeRead for TxInGen {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
Ok(TxInGen {
height: r.read_varint()?,
})
}
}
#[derive(Clone, Debug)]
pub struct TxInToKey {
pub amount: u64,
pub key_offsets: Vec<Variant>,
pub k_image: Point,
pub etc_details: Vec<Variant>,
}
impl EpeeWrite for TxInToKey {
fn write_epee(&self, out: &mut Vec<u8>) {
append_varint(out, self.amount);
write_vec(&self.key_offsets, out);
self.k_image.write_epee(out);
write_vec(&self.etc_details, out);
}
}
impl EpeeRead for TxInToKey {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
Ok(TxInToKey {
amount: r.read_varint()?,
key_offsets: r.read_vec()?,
k_image: Point::read_epee(r)?,
etc_details: r.read_vec()?,
})
}
}
#[derive(Clone, Debug)]
pub struct TxInZcInput {
pub key_offsets: Vec<Variant>,
pub key_image: Point,
pub etc_details: Vec<Variant>,
}
impl EpeeWrite for TxInZcInput {
fn write_epee(&self, out: &mut Vec<u8>) {
write_vec(&self.key_offsets, out);
self.key_image.write_epee(out);
write_vec(&self.etc_details, out);
}
}
impl EpeeRead for TxInZcInput {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
Ok(TxInZcInput {
key_offsets: r.read_vec()?,
key_image: Point::read_epee(r)?,
etc_details: r.read_vec()?,
})
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TxOutZarcanum {
pub stealth_address: Value256,
pub concealing_point: Value256,
pub amount_commitment: Value256,
pub blinded_asset_id: Value256,
pub encrypted_amount: u64,
pub mix_attr: u8,
}
impl EpeeWrite for TxOutZarcanum {
fn write_epee(&self, out: &mut Vec<u8>) {
self.stealth_address.write_epee(out);
self.concealing_point.write_epee(out);
self.amount_commitment.write_epee(out);
self.blinded_asset_id.write_epee(out);
self.encrypted_amount.write_epee(out);
out.push(self.mix_attr);
}
}
impl EpeeRead for TxOutZarcanum {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
Ok(TxOutZarcanum {
stealth_address: Value256::read_epee(r)?,
concealing_point: Value256::read_epee(r)?,
amount_commitment: Value256::read_epee(r)?,
blinded_asset_id: Value256::read_epee(r)?,
encrypted_amount: u64::read_epee(r)?,
mix_attr: r.read_byte()?,
})
}
}
#[derive(Clone, Debug, Default)]
pub struct TransactionPrefix {
pub version: u64,
pub vin: Vec<Variant>,
pub extra: Vec<Variant>,
pub vout: Vec<Variant>,
pub hardfork_id: u8,
}
impl EpeeWrite for TransactionPrefix {
fn write_epee(&self, out: &mut Vec<u8>) {
append_varint(out, self.version);
write_vec(&self.vin, out);
write_vec(&self.extra, out);
write_vec(&self.vout, out);
if self.version >= TRANSACTION_VERSION_POST_HF5 {
out.push(self.hardfork_id);
}
}
}
impl EpeeRead for TransactionPrefix {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
let version = r.read_varint()?;
let vin = r.read_vec()?;
let extra = r.read_vec()?;
let vout = r.read_vec()?;
let hardfork_id = if version >= TRANSACTION_VERSION_POST_HF5 {
r.read_byte()?
} else {
0
};
Ok(TransactionPrefix {
version,
vin,
extra,
vout,
hardfork_id,
})
}
}
impl TransactionPrefix {
pub fn hash(&self) -> Value256 {
Value256(purecrypto::hash::keccak256(&self.to_epee_bytes()))
}
}
#[derive(Clone, Debug, Default)]
pub struct Transaction {
pub version: u64,
pub vin: Vec<Variant>,
pub extra: Vec<Variant>,
pub vout: Vec<Variant>,
pub hardfork_id: u8,
pub attachment: Vec<Variant>,
pub signatures: Vec<Variant>,
pub proofs: Vec<Variant>,
}
impl Transaction {
pub fn prefix(&self) -> TransactionPrefix {
TransactionPrefix {
version: self.version,
vin: self.vin.clone(),
extra: self.extra.clone(),
vout: self.vout.clone(),
hardfork_id: self.hardfork_id,
}
}
pub fn id(&self) -> Value256 {
self.prefix().hash()
}
pub fn fee(&self) -> Option<u64> {
self.extra.iter().find_map(|e| match e {
Variant::ZarcanumTxDataV1 { fee } => Some(*fee),
_ => None,
})
}
pub fn tx_pub_key(&self) -> Option<Value256> {
self.extra.iter().find_map(|e| match e {
Variant::PubKey(v) => Some(*v),
_ => None,
})
}
pub fn deserialize_for_scan(buf: &[u8]) -> Result<Transaction> {
let mut r = Reader::new(buf);
let version = r.read_varint()?;
let vin = r.read_vec()?;
let extra = r.read_vec()?;
let vout = r.read_vec()?;
let hardfork_id = if version >= TRANSACTION_VERSION_POST_HF5 {
r.read_byte()?
} else {
0
};
let attachment = r.read_vec()?;
Ok(Transaction {
version,
vin,
extra,
vout,
hardfork_id,
attachment,
signatures: Vec::new(),
proofs: Vec::new(),
})
}
pub fn zc_inputs_count(&self) -> usize {
self.vin
.iter()
.filter(|v| v.tag() == tag::TXIN_ZC_INPUT)
.count()
}
}
impl EpeeWrite for Transaction {
fn write_epee(&self, out: &mut Vec<u8>) {
append_varint(out, self.version);
write_vec(&self.vin, out);
write_vec(&self.extra, out);
write_vec(&self.vout, out);
if self.version >= TRANSACTION_VERSION_POST_HF5 {
out.push(self.hardfork_id);
}
write_vec(&self.attachment, out);
write_vec(&self.signatures, out);
write_vec(&self.proofs, out);
}
}
impl EpeeRead for Transaction {
fn read_epee(r: &mut Reader<'_>) -> Result<Self> {
let version = r.read_varint()?;
let vin = r.read_vec()?;
let extra = r.read_vec()?;
let vout = r.read_vec()?;
let hardfork_id = if version >= TRANSACTION_VERSION_POST_HF5 {
r.read_byte()?
} else {
0
};
Ok(Transaction {
version,
vin,
extra,
vout,
hardfork_id,
attachment: r.read_vec()?,
signatures: r.read_vec()?,
proofs: r.read_vec()?,
})
}
}