188 lines
6.2 KiB
Rust
188 lines
6.2 KiB
Rust
use std::{borrow::Cow, fmt::Display, ops::Deref, str::FromStr};
|
|
|
|
use pallas_codec::utils::CborWrap;
|
|
use pallas_crypto::hash::Hash;
|
|
use pallas_primitives::{alonzo, byron};
|
|
|
|
use crate::{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 std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}#{}", self.hash(), self.index())
|
|
}
|
|
}
|
|
|
|
impl FromStr for OutputRef {
|
|
type Err = crate::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::Error::invalid_utxo_ref(s))?,
|
|
u64::from_str(b).map_err(|_| crate::Error::invalid_utxo_ref(s))?,
|
|
),
|
|
_ => return Err(crate::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 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::*;
|
|
|
|
#[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);
|
|
}
|
|
}
|
|
}
|