use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
use core::{fmt, fmt::Display, ops::Deref, str::FromStr};
use crate::pallas_codec::utils::CborWrap;
use crate::pallas_crypto::hash::Hash;
use crate::pallas_primitives::{alonzo, byron};
use crate::pallas_traverse::{MultiEraInput, OutputRef};
impl OutputRef {
pub fn new(hash: Hash<32>, index: u64) -> Self {
Self(hash, index)
}
pub fn hash(&self) -> &Hash<32> {
&self.0
}
pub fn index(&self) -> u64 {
self.1
}
}
impl Display for OutputRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}#{}", self.hash(), self.index())
}
}
impl FromStr for OutputRef {
type Err = crate::pallas_traverse::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.trim().split('#').collect();
let (hash, idx) = match &parts[..] {
&[a, b] => (
Hash::<32>::from_str(a)
.map_err(|_| crate::pallas_traverse::Error::invalid_utxo_ref(s))?,
u64::from_str(b).map_err(|_| crate::pallas_traverse::Error::invalid_utxo_ref(s))?,
),
_ => return Err(crate::pallas_traverse::Error::invalid_utxo_ref(s)),
};
Ok(Self::new(hash, idx))
}
}
impl<'b> MultiEraInput<'b> {
pub fn from_byron(input: &'b byron::TxIn) -> Self {
Self::Byron(Box::new(Cow::Borrowed(input)))
}
pub fn from_alonzo_compatible(input: &'b alonzo::TransactionInput) -> Self {
Self::AlonzoCompatible(Box::new(Cow::Borrowed(input)))
}
pub fn output_ref(&self) -> OutputRef {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((tx, idx))) => OutputRef(*tx, *idx as u64),
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => OutputRef(x.transaction_id, x.index),
}
}
pub fn lexicographical_key(&self) -> String {
format!("{}#{}", self.hash(), self.index())
}
pub fn hash(&self) -> &Hash<32> {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((x, _))) => x,
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => &x.transaction_id,
}
}
pub fn index(&self) -> u64 {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((_, x))) => *x as u64,
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => x.index,
}
}
pub fn as_alonzo(&self) -> Option<&alonzo::TransactionInput> {
match self {
MultiEraInput::Byron(_) => None,
MultiEraInput::AlonzoCompatible(x) => Some(x),
}
}
pub fn as_byron(&self) -> Option<&byron::TxIn> {
match self {
MultiEraInput::Byron(x) => Some(x),
MultiEraInput::AlonzoCompatible(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::pallas_traverse::*;
#[test]
fn test_expected_values() {
let blocks = vec![
include_str!("../../test_data/byron2.block"),
include_str!("../../test_data/alonzo1.block"),
];
let mut expected = vec![
"da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5#14",
"e059de2179400cd7e81ddb6683c0136c9d68119ff3a27a472ad2d98e2f1fbc9c#3",
"adeb5745e6dba2c05a98f0ad9162b947f1484e998b8b3335f98213e0c67f426e#0",
"f0fb258a6e741a02ae91b8dc7fe340b9e5b601a6048bf2a0c205f9cc6f51768d#1",
"c2e4e1f1d8217724b76d979166b16cb0cf5cd6506f70f48c618a085b10460c44#2",
"aaca2f41f4a17fe464481c69f1220a7bfd93b1a6854f52006094271204e7df7c#0",
"89185f2daf9ea3bdfdb5d1fef7eced7e890cb89b8821275c0bf0973be08c4ee9#1",
"bf1f12a83095ac6738ecce5e3e540ad2cff160c46af9137eb6dc0b971f0ac5de#0",
"df4ebe9ac3ad31a55a06f3e51ca0dbaa947aaf25857ab3a12fe9315cabec11d3#0",
"087138a5596168650835c8c00f488e167e869bd991ef0683d2dbf3696b0e6650#1",
"cc9f28625de0b5b9bbe8f61c9332bfda2c987162f85d2e42e437666c27826573#0",
"d0965859ce9b3025ccbe64f24e3cb30f7400252eb3e235c3604986c2fdd755db#1",
];
for block_str in blocks {
let cbor = hex::decode(block_str).expect("invalid hex");
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
for tx in block.txs() {
for input in tx.inputs() {
let ref_ = input.output_ref();
let right = expected.remove(0);
assert_eq!(ref_.to_string(), right);
}
}
}
}
#[test]
fn test_duplicate_consumed_inputs() {
let tx_bytecode_hex = include_str!("../../test_data/duplicateinput.tx");
let bytecode = hex::decode(tx_bytecode_hex).unwrap();
let tx = MultiEraTx::decode_for_era(Era::Alonzo, &bytecode).unwrap();
let expected_inputs = vec![
"4d9cb6bf1c2e349f1bcd454a632d2b721d5badcf687220430c316588f39506ab#1",
"4d9cb6bf1c2e349f1bcd454a632d2b721d5badcf687220430c316588f39506ab#1",
];
let inputs: Vec<String> = tx
.inputs()
.into_iter()
.map(|x| x.output_ref().to_string())
.collect();
let expected_consumed =
vec!["4d9cb6bf1c2e349f1bcd454a632d2b721d5badcf687220430c316588f39506ab#1"];
let consumed: Vec<String> = tx
.consumes()
.into_iter()
.map(|x| x.output_ref().to_string())
.collect();
assert_eq!(inputs, expected_inputs);
assert_eq!(consumed, expected_consumed);
}
#[test]
fn test_utxo_ref_parsing() {
let valid_vectors = [
"da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5#14",
" da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5#14 ",
];
for vector in valid_vectors.iter() {
let sample = OutputRef::from_str(vector).unwrap();
assert_eq!(
sample.hash().to_string(),
"da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5"
);
assert_eq!(sample.index(), 14);
}
}
}