diff --git a/pallas-codec/src/utils.rs b/pallas-codec/src/utils.rs
index 46fadb0b..9f90d772 100644
--- a/pallas-codec/src/utils.rs
+++ b/pallas-codec/src/utils.rs
@@ -386,6 +386,16 @@ impl MaybeIndefArray {
pub fn to_vec(self) -> Vec {
self.into()
}
+
+ pub fn map_into(self, f: F) -> MaybeIndefArray
+ where
+ F: FnMut(A) -> B,
+ {
+ match self {
+ MaybeIndefArray::Def(x) => MaybeIndefArray::Def(x.into_iter().map(f).collect()),
+ MaybeIndefArray::Indef(x) => MaybeIndefArray::Indef(x.into_iter().map(f).collect()),
+ }
+ }
}
impl Deref for MaybeIndefArray {
@@ -1157,6 +1167,71 @@ impl minicbor::Encode for KeepRaw<'_, T> {
}
}
+/// Decodes just a raw bytes with skipping actual decoding of the CBOR object.
+/// Stores the original CBOR bytes for further decoding.
+///
+/// # Examples
+///
+/// ```
+/// use pallas_codec::utils::OnlyRaw;
+///
+/// let a = (123u16, (456u16, 789u16), 123u16);
+/// let data = minicbor::to_vec(a).unwrap();
+///
+/// let (_, keeper, _): (u16, OnlyRaw<(u16, u16)>, u16) = minicbor::decode(&data).unwrap();
+/// let confirm: (u16, u16) = keeper.decode().unwrap();
+/// assert_eq!(confirm, (456u16, 789u16));
+/// ```
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct OnlyRaw<'b, T> {
+ raw: &'b [u8],
+ _p: std::marker::PhantomData,
+}
+
+impl<'b, T> OnlyRaw<'b, T> {
+ pub fn raw_cbor(&self) -> &'b [u8] {
+ self.raw
+ }
+}
+
+impl<'b, T> OnlyRaw<'b, T>
+where
+ T: minicbor::Decode<'b, ()>,
+{
+ pub fn decode(&self) -> Result {
+ minicbor::decode(self.raw)
+ }
+}
+
+impl<'b, T, C> minicbor::Decode<'b, C> for OnlyRaw<'b, T>
+where
+ T: minicbor::Decode<'b, C>,
+{
+ fn decode(d: &mut minicbor::Decoder<'b>, _: &mut C) -> Result {
+ let all = d.input();
+ let start = d.position();
+ d.skip()?;
+ let end = d.position();
+
+ Ok(Self {
+ raw: &all[start..end],
+ _p: std::marker::PhantomData,
+ })
+ }
+}
+
+impl minicbor::Encode for OnlyRaw<'_, T> {
+ fn encode(
+ &self,
+ e: &mut minicbor::Encoder,
+ _ctx: &mut C,
+ ) -> Result<(), minicbor::encode::Error> {
+ e.writer_mut()
+ .write_all(self.raw_cbor())
+ .map_err(minicbor::encode::Error::write)
+ }
+}
+
/// Struct to hold arbitrary CBOR to be processed independently
///
/// # Examples
diff --git a/pallas-primitives/Cargo.toml b/pallas-primitives/Cargo.toml
index 30dc0d3c..706751dc 100644
--- a/pallas-primitives/Cargo.toml
+++ b/pallas-primitives/Cargo.toml
@@ -13,6 +13,10 @@ authors = [
"Lucas Rosa ",
]
+[[bench]]
+name = "alonzo_decoding"
+harness = false
+
[dependencies]
hex = "0.4.3"
log = "0.4.14"
@@ -23,6 +27,9 @@ bech32 = "0.9.0"
serde = { version = "1.0.136", optional = true, features = ["derive"] }
serde_json = { version = "1.0.79", optional = true }
+[dev-dependencies]
+criterion = { version = "0.5.1" }
+
[features]
json = ["serde", "serde_json"]
default = ["json"]
diff --git a/pallas-primitives/benches/alonzo_decoding.rs b/pallas-primitives/benches/alonzo_decoding.rs
new file mode 100644
index 00000000..6b9641a5
--- /dev/null
+++ b/pallas-primitives/benches/alonzo_decoding.rs
@@ -0,0 +1,30 @@
+use criterion::{criterion_group, criterion_main, Criterion};
+use pallas_codec::utils::{KeepRaw, OnlyRaw};
+use pallas_primitives::alonzo::AuxiliaryData;
+
+const AUXILARY_HEX: &'static str = "d90103a100a11902d1a278386238663665376634326563336264303239363662343034313131616530653233636238356539303236343462666237636539343036303766a1781c63756d44796e616d6963496e746567726174696f6e456e67696e6565a76538316e6f6e6868617264776172656b383973696d696c697175656d41646d696e6973747261746f726b6465736372697074696f6e783e4d79206e65696768626f7220416c69646120686173206f6e65206f662074686573652e2053686520776f726b7320617320612067616d626c657220616e646566696c657383a3696d656469615479706569696d6167652f706e67646e616d65781c63756d44796e616d6963496e746567726174696f6e456e67696e6565637372637835697066733a2f2f516d5a46373437504659565a6d5161363163777351555268586131783634344b415631377778766b454238756577a3696d656469615479706569696d6167652f706e67646e616d65781c63756d44796e616d6963496e746567726174696f6e456e67696e6565637372637835697066733a2f2f516d644633466b59395277686d74783636534e773373637555756f444852745439684e4d36646f63553767326544a3696d656469615479706569696d6167652f706e67646e616d65781c63756d44796e616d6963496e746567726174696f6e456e67696e6565637372637835697066733a2f2f516d62614a75504e336a463857535544667659687a75324b5a716d6a517868506135736d666245445363366a695765696d6167657835697066733a2f2f516d6156524a7a343652384e72476b4a58684c6170316145323642676a365367746564393442734a487576437556696d656469615479706569696d6167652f706e67646e616d65781c63756d44796e616d6963496e746567726174696f6e456e67696e65656776657273696f6e63312e30";
+
+fn auxilary_data_benches(c: &mut Criterion) {
+ let bytes = hex::decode(AUXILARY_HEX).unwrap();
+
+ let mut group = c.benchmark_group("Alonzo Auxilary Data Decoding");
+ group.bench_function("KeepRaw", |b| {
+ b.iter(|| {
+ let _aux: KeepRaw =
+ pallas_codec::minicbor::decode(bytes.as_slice()).unwrap();
+ });
+ });
+
+ group.bench_function("OnlyRaw", |b| {
+ b.iter(|| {
+ let _aux: OnlyRaw =
+ pallas_codec::minicbor::decode(bytes.as_slice()).unwrap();
+ });
+ });
+
+ group.finish();
+}
+
+criterion_group!(benches, auxilary_data_benches);
+
+criterion_main!(benches);
\ No newline at end of file
diff --git a/pallas-primitives/src/alonzo/model.rs b/pallas-primitives/src/alonzo/model.rs
index ba47f0d1..8cabfc28 100644
--- a/pallas-primitives/src/alonzo/model.rs
+++ b/pallas-primitives/src/alonzo/model.rs
@@ -10,9 +10,10 @@ pub use crate::{
plutus_data::*, AddrKeyhash, AssetName, Bytes, Coin, CostModel, DatumHash, DnsName, Epoch,
ExUnitPrices, ExUnits, GenesisDelegateHash, Genesishash, Hash, IPv4, IPv6, Int, KeepRaw,
KeyValuePairs, MaybeIndefArray, Metadata, Metadatum, MetadatumLabel, NetworkId, Nonce,
- NonceVariant, Nullable, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata, PoolMetadataHash,
- Port, PositiveInterval, ProtocolVersion, RationalNumber, Relay, RewardAccount, ScriptHash,
- StakeCredential, TransactionIndex, TransactionInput, UnitInterval, VrfCert, VrfKeyhash,
+ NonceVariant, Nullable, OnlyRaw, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata,
+ PoolMetadataHash, Port, PositiveInterval, ProtocolVersion, RationalNumber, Relay,
+ RewardAccount, ScriptHash, StakeCredential, TransactionIndex, TransactionInput, UnitInterval,
+ VrfCert, VrfKeyhash,
};
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
@@ -823,63 +824,58 @@ impl minicbor::Encode for AuxiliaryData {
}
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
-pub struct Block {
+pub struct PseudoBlock
+where
+ T1: std::clone::Clone,
+ T2: std::clone::Clone,
+ T3: std::clone::Clone,
+ T4: std::clone::Clone,
+{
#[n(0)]
- pub header: Header,
+ pub header: T1,
#[b(1)]
- pub transaction_bodies: Vec,
+ pub transaction_bodies: MaybeIndefArray,
#[n(2)]
- pub transaction_witness_sets: Vec,
+ pub transaction_witness_sets: MaybeIndefArray,
#[n(3)]
- pub auxiliary_data_set: KeyValuePairs,
+ pub auxiliary_data_set: KeyValuePairs,
#[n(4)]
- pub invalid_transactions: Option>,
+ pub invalid_transactions: Option>,
}
+pub type Block = PseudoBlock;
+
/// A memory representation of an already minted block
///
/// This structure is analogous to [Block], but it allows to retrieve the
/// original CBOR bytes for each structure that might require hashing. In this
/// way, we make sure that the resulting hash matches what exists on-chain.
-#[derive(Encode, Decode, Debug, PartialEq, Clone)]
-pub struct MintedBlock<'b> {
- #[n(0)]
- pub header: KeepRaw<'b, MintedHeader<'b>>,
-
- #[b(1)]
- pub transaction_bodies: MaybeIndefArray>,
-
- #[n(2)]
- pub transaction_witness_sets: MaybeIndefArray>>,
-
- #[n(3)]
- pub auxiliary_data_set: KeyValuePairs>,
-
- #[n(4)]
- pub invalid_transactions: Option>,
-}
+pub type MintedBlock<'b> = PseudoBlock<
+ KeepRaw<'b, MintedHeader<'b>>,
+ KeepRaw<'b, TransactionBody>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ KeepRaw<'b, AuxiliaryData>,
+>;
+
+pub type MintedBlockWithRawAuxiliary<'b> = PseudoBlock<
+ KeepRaw<'b, MintedHeader<'b>>,
+ KeepRaw<'b, TransactionBody>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
impl<'b> From> for Block {
fn from(x: MintedBlock<'b>) -> Self {
Block {
header: x.header.unwrap().into(),
- transaction_bodies: x
- .transaction_bodies
- .to_vec()
- .into_iter()
- .map(|x| x.unwrap())
- .collect(),
+ transaction_bodies: x.transaction_bodies.map_into(|x| x.unwrap()),
transaction_witness_sets: x
.transaction_witness_sets
- .to_vec()
- .into_iter()
- .map(|x| x.unwrap())
- .map(WitnessSet::from)
- .collect(),
+ .map_into(|x| WitnessSet::from(x.unwrap())),
auxiliary_data_set: x
.auxiliary_data_set
.to_vec()
@@ -892,35 +888,39 @@ impl<'b> From> for Block {
}
}
-#[derive(Serialize, Deserialize, Encode, Decode, Debug)]
-pub struct Tx {
- #[n(0)]
- pub transaction_body: TransactionBody,
+#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone)]
+pub struct PseudoTx
+where
+ T1: std::clone::Clone,
+ T2: std::clone::Clone,
+ T3: std::clone::Clone,
+{
+ #[b(0)]
+ pub transaction_body: T1,
#[n(1)]
- pub transaction_witness_set: WitnessSet,
+ pub transaction_witness_set: T2,
#[n(2)]
pub success: bool,
#[n(3)]
- pub auxiliary_data: Nullable,
+ pub auxiliary_data: Nullable,
}
-#[derive(Encode, Decode, Debug, Clone)]
-pub struct MintedTx<'b> {
- #[b(0)]
- pub transaction_body: KeepRaw<'b, TransactionBody>,
-
- #[n(1)]
- pub transaction_witness_set: KeepRaw<'b, MintedWitnessSet<'b>>,
+pub type Tx = PseudoTx;
- #[n(2)]
- pub success: bool,
+pub type MintedTx<'b> = PseudoTx<
+ KeepRaw<'b, TransactionBody>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ KeepRaw<'b, AuxiliaryData>,
+>;
- #[n(3)]
- pub auxiliary_data: Nullable>,
-}
+pub type MintedTxWithRawAuxiliary<'b> = PseudoTx<
+ KeepRaw<'b, TransactionBody>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
#[cfg(test)]
mod tests {
@@ -928,59 +928,75 @@ mod tests {
use crate::{alonzo::PlutusData, Fragment};
- use super::{Header, MintedBlock};
-
- type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ use super::{Header, MintedBlock, MintedBlockWithRawAuxiliary};
+
+ const TEST_BLOCKS: [&'static str; 25] = [
+ include_str!("../../../test_data/alonzo1.block"),
+ include_str!("../../../test_data/alonzo2.block"),
+ include_str!("../../../test_data/alonzo3.block"),
+ include_str!("../../../test_data/alonzo4.block"),
+ include_str!("../../../test_data/alonzo5.block"),
+ include_str!("../../../test_data/alonzo6.block"),
+ include_str!("../../../test_data/alonzo7.block"),
+ include_str!("../../../test_data/alonzo8.block"),
+ include_str!("../../../test_data/alonzo9.block"),
+ // old block without invalid_transactions fields
+ include_str!("../../../test_data/alonzo10.block"),
+ // peculiar block with protocol update params
+ include_str!("../../../test_data/alonzo11.block"),
+ // peculiar block with decoding issue
+ // https://github.com/txpipe/oura/issues/37
+ include_str!("../../../test_data/alonzo12.block"),
+ // peculiar block with protocol update params, including nonce
+ include_str!("../../../test_data/alonzo13.block"),
+ // peculiar block with overflow crash
+ // https://github.com/txpipe/oura/issues/113
+ include_str!("../../../test_data/alonzo14.block"),
+ // peculiar block with many move-instantaneous-rewards certs
+ include_str!("../../../test_data/alonzo15.block"),
+ // peculiar block with protocol update values
+ include_str!("../../../test_data/alonzo16.block"),
+ // peculiar block with missing nonce hash
+ include_str!("../../../test_data/alonzo17.block"),
+ // peculiar block with strange AuxiliaryData variant
+ include_str!("../../../test_data/alonzo18.block"),
+ // peculiar block with strange AuxiliaryData variant
+ include_str!("../../../test_data/alonzo18.block"),
+ // peculiar block with nevative i64 overflow
+ include_str!("../../../test_data/alonzo19.block"),
+ // peculiar block with very BigInt in plutus code
+ include_str!("../../../test_data/alonzo20.block"),
+ // peculiar block with bad tx hash
+ include_str!("../../../test_data/alonzo21.block"),
+ // peculiar block with bad tx hash
+ include_str!("../../../test_data/alonzo22.block"),
+ // peculiar block with indef byte array in plutus data
+ include_str!("../../../test_data/alonzo23.block"),
+ // peculiar block with invalid address (pointer overflow)
+ include_str!("../../../test_data/alonzo27.block"),
+ ];
#[test]
fn block_isomorphic_decoding_encoding() {
- let test_blocks = vec![
- include_str!("../../../test_data/alonzo1.block"),
- include_str!("../../../test_data/alonzo2.block"),
- include_str!("../../../test_data/alonzo3.block"),
- include_str!("../../../test_data/alonzo4.block"),
- include_str!("../../../test_data/alonzo5.block"),
- include_str!("../../../test_data/alonzo6.block"),
- include_str!("../../../test_data/alonzo7.block"),
- include_str!("../../../test_data/alonzo8.block"),
- include_str!("../../../test_data/alonzo9.block"),
- // old block without invalid_transactions fields
- include_str!("../../../test_data/alonzo10.block"),
- // peculiar block with protocol update params
- include_str!("../../../test_data/alonzo11.block"),
- // peculiar block with decoding issue
- // https://github.com/txpipe/oura/issues/37
- include_str!("../../../test_data/alonzo12.block"),
- // peculiar block with protocol update params, including nonce
- include_str!("../../../test_data/alonzo13.block"),
- // peculiar block with overflow crash
- // https://github.com/txpipe/oura/issues/113
- include_str!("../../../test_data/alonzo14.block"),
- // peculiar block with many move-instantaneous-rewards certs
- include_str!("../../../test_data/alonzo15.block"),
- // peculiar block with protocol update values
- include_str!("../../../test_data/alonzo16.block"),
- // peculiar block with missing nonce hash
- include_str!("../../../test_data/alonzo17.block"),
- // peculiar block with strange AuxiliaryData variant
- include_str!("../../../test_data/alonzo18.block"),
- // peculiar block with strange AuxiliaryData variant
- include_str!("../../../test_data/alonzo18.block"),
- // peculiar block with nevative i64 overflow
- include_str!("../../../test_data/alonzo19.block"),
- // peculiar block with very BigInt in plutus code
- include_str!("../../../test_data/alonzo20.block"),
- // peculiar block with bad tx hash
- include_str!("../../../test_data/alonzo21.block"),
- // peculiar block with bad tx hash
- include_str!("../../../test_data/alonzo22.block"),
- // peculiar block with indef byte array in plutus data
- include_str!("../../../test_data/alonzo23.block"),
- // peculiar block with invalid address (pointer overflow)
- include_str!("../../../test_data/alonzo27.block"),
- ];
+ type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
+ println!("decoding test block {}", idx + 1);
+ let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
- for (idx, block_str) in test_blocks.iter().enumerate() {
+ let block: BlockWrapper = minicbor::decode(&bytes[..])
+ .unwrap_or_else(|_| panic!("error decoding cbor for file {idx}"));
+
+ let bytes2 = to_vec(block)
+ .unwrap_or_else(|_| panic!("error encoding block cbor for file {idx}"));
+
+ assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
+ }
+ }
+
+ #[test]
+ fn block_with_raw_aux_isomorphic_decoding_encoding() {
+ type BlockWrapper<'b> = (u16, MintedBlockWithRawAuxiliary<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
println!("decoding test block {}", idx + 1);
let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
diff --git a/pallas-primitives/src/babbage/model.rs b/pallas-primitives/src/babbage/model.rs
index acc937f3..c05c81e7 100644
--- a/pallas-primitives/src/babbage/model.rs
+++ b/pallas-primitives/src/babbage/model.rs
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use pallas_codec::{
minicbor::{self, Decode, Encode},
- utils::{Bytes, CborWrap, KeepRaw, KeyValuePairs, MaybeIndefArray, Nullable},
+ utils::{Bytes, CborWrap, KeepRaw, KeyValuePairs, MaybeIndefArray, Nullable, OnlyRaw},
};
use pallas_crypto::hash::{Hash, Hasher};
@@ -659,6 +659,13 @@ pub type MintedBlock<'b> = PseudoBlock<
KeepRaw<'b, AuxiliaryData>,
>;
+pub type MintedBlockWithRawAuxiliary<'b> = PseudoBlock<
+ KeepRaw<'b, MintedHeader<'b>>,
+ KeepRaw<'b, MintedTransactionBody<'b>>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
+
impl<'b> From> for Block {
fn from(x: MintedBlock<'b>) -> Self {
Block {
@@ -719,6 +726,12 @@ pub type MintedTx<'b> = PseudoTx<
KeepRaw<'b, AuxiliaryData>,
>;
+pub type MintedTxWithRawAuxiliary<'b> = PseudoTx<
+ KeepRaw<'b, MintedTransactionBody<'b>>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
+
impl<'b> From> for Tx {
fn from(x: MintedTx<'b>) -> Self {
Tx {
@@ -734,34 +747,50 @@ impl<'b> From> for Tx {
mod tests {
use pallas_codec::minicbor;
- use super::{MintedBlock, TransactionOutput};
+ use super::{MintedBlock, MintedBlockWithRawAuxiliary, TransactionOutput};
use crate::Fragment;
- type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ const TEST_BLOCKS: [&'static str; 10] = [
+ include_str!("../../../test_data/babbage1.block"),
+ include_str!("../../../test_data/babbage2.block"),
+ include_str!("../../../test_data/babbage3.block"),
+ // peculiar block with single plutus cost model
+ include_str!("../../../test_data/babbage4.block"),
+ // peculiar block with i32 overlfow
+ include_str!("../../../test_data/babbage5.block"),
+ // peculiar block with map undef in plutus data
+ include_str!("../../../test_data/babbage6.block"),
+ // block with generic int in cbor
+ include_str!("../../../test_data/babbage7.block"),
+ // block with indef bytes for plutus data bignum
+ include_str!("../../../test_data/babbage8.block"),
+ // block with inline datum that fails hashes
+ include_str!("../../../test_data/babbage9.block"),
+ // block with pool margin numerator greater than i64::MAX
+ include_str!("../../../test_data/babbage10.block"),
+ ];
#[test]
fn block_isomorphic_decoding_encoding() {
- let test_blocks = [
- include_str!("../../../test_data/babbage1.block"),
- include_str!("../../../test_data/babbage2.block"),
- include_str!("../../../test_data/babbage3.block"),
- // peculiar block with single plutus cost model
- include_str!("../../../test_data/babbage4.block"),
- // peculiar block with i32 overlfow
- include_str!("../../../test_data/babbage5.block"),
- // peculiar block with map undef in plutus data
- include_str!("../../../test_data/babbage6.block"),
- // block with generic int in cbor
- include_str!("../../../test_data/babbage7.block"),
- // block with indef bytes for plutus data bignum
- include_str!("../../../test_data/babbage8.block"),
- // block with inline datum that fails hashes
- include_str!("../../../test_data/babbage9.block"),
- // block with pool margin numerator greater than i64::MAX
- include_str!("../../../test_data/babbage10.block"),
- ];
-
- for (idx, block_str) in test_blocks.iter().enumerate() {
+ type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
+ println!("decoding test block {}", idx + 1);
+ let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
+
+ let block: BlockWrapper = minicbor::decode(&bytes[..])
+ .unwrap_or_else(|e| panic!("error decoding cbor for file {idx}: {e:?}"));
+
+ let bytes2 = minicbor::to_vec(block)
+ .unwrap_or_else(|e| panic!("error encoding block cbor for file {idx}: {e:?}"));
+
+ assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
+ }
+ }
+
+ #[test]
+ fn block_with_raw_aux_isomorphic_decoding_encoding() {
+ type BlockWrapper<'b> = (u16, MintedBlockWithRawAuxiliary<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
println!("decoding test block {}", idx + 1);
let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
diff --git a/pallas-primitives/src/conway/model.rs b/pallas-primitives/src/conway/model.rs
index c758bade..082ddc75 100644
--- a/pallas-primitives/src/conway/model.rs
+++ b/pallas-primitives/src/conway/model.rs
@@ -11,7 +11,7 @@ pub use crate::{
plutus_data::*, AddrKeyhash, AssetName, Bytes, Coin, CostModel, DnsName, Epoch, ExUnits,
GenesisDelegateHash, Genesishash, Hash, IPv4, IPv6, KeepRaw, KeyValuePairs, MaybeIndefArray,
Metadata, Metadatum, MetadatumLabel, NetworkId, NonEmptyKeyValuePairs, NonEmptySet, NonZeroInt,
- Nonce, NonceVariant, Nullable, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata,
+ Nonce, NonceVariant, Nullable, OnlyRaw, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata,
PoolMetadataHash, Port, PositiveCoin, PositiveInterval, ProtocolVersion, RationalNumber, Relay,
RewardAccount, ScriptHash, Set, StakeCredential, TransactionIndex, TransactionInput,
UnitInterval, VrfCert, VrfKeyhash,
@@ -1547,6 +1547,13 @@ pub type MintedBlock<'b> = PseudoBlock<
KeepRaw<'b, AuxiliaryData>,
>;
+pub type MintedBlockWithRawAuxiliary<'b> = PseudoBlock<
+ KeepRaw<'b, MintedHeader<'b>>,
+ KeepRaw<'b, MintedTransactionBody<'b>>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
+
impl<'b> From> for Block {
fn from(x: MintedBlock<'b>) -> Self {
Block {
@@ -1607,6 +1614,12 @@ pub type MintedTx<'b> = PseudoTx<
KeepRaw<'b, AuxiliaryData>,
>;
+pub type MintedTxWithRawAuxiliary<'b> = PseudoTx<
+ KeepRaw<'b, MintedTransactionBody<'b>>,
+ KeepRaw<'b, MintedWitnessSet<'b>>,
+ OnlyRaw<'b, AuxiliaryData>,
+>;
+
impl<'b> From> for Tx {
fn from(x: MintedTx<'b>) -> Self {
Tx {
@@ -1622,22 +1635,38 @@ impl<'b> From> for Tx {
mod tests {
use pallas_codec::minicbor;
- use super::MintedBlock;
+ use super::{MintedBlock, MintedBlockWithRawAuxiliary};
- type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ const TEST_BLOCKS: [&'static str; 4] = [
+ include_str!("../../../test_data/conway1.block"),
+ include_str!("../../../test_data/conway2.block"),
+ // interesting block with extreme values
+ include_str!("../../../test_data/conway3.block"),
+ // interesting block with extreme values
+ include_str!("../../../test_data/conway4.block"),
+ ];
#[test]
fn block_isomorphic_decoding_encoding() {
- let test_blocks = [
- include_str!("../../../test_data/conway1.block"),
- include_str!("../../../test_data/conway2.block"),
- // interesting block with extreme values
- include_str!("../../../test_data/conway3.block"),
- // interesting block with extreme values
- include_str!("../../../test_data/conway4.block"),
- ];
-
- for (idx, block_str) in test_blocks.iter().enumerate() {
+ type BlockWrapper<'b> = (u16, MintedBlock<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
+ println!("decoding test block {}", idx + 1);
+ let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
+
+ let block: BlockWrapper = minicbor::decode(&bytes)
+ .unwrap_or_else(|e| panic!("error decoding cbor for file {idx}: {e:?}"));
+
+ let bytes2 = minicbor::to_vec(block)
+ .unwrap_or_else(|e| panic!("error encoding block cbor for file {idx}: {e:?}"));
+
+ assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
+ }
+ }
+
+ #[test]
+ fn block_with_raw_aux_isomorphic_decoding_encoding() {
+ type BlockWrapper<'b> = (u16, MintedBlockWithRawAuxiliary<'b>);
+ for (idx, block_str) in TEST_BLOCKS.iter().enumerate() {
println!("decoding test block {}", idx + 1);
let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
diff --git a/pallas-primitives/src/lib.rs b/pallas-primitives/src/lib.rs
index accbf414..18f80066 100644
--- a/pallas-primitives/src/lib.rs
+++ b/pallas-primitives/src/lib.rs
@@ -13,7 +13,7 @@ pub use framework::*;
pub use pallas_codec::utils::{
Bytes, Int, KeepRaw, KeyValuePairs, MaybeIndefArray, NonEmptyKeyValuePairs, NonEmptySet,
- NonZeroInt, Nullable, PositiveCoin, Set,
+ NonZeroInt, Nullable, OnlyRaw, PositiveCoin, Set,
};
pub use pallas_crypto::hash::Hash;
diff --git a/pallas-traverse/src/block.rs b/pallas-traverse/src/block.rs
index 5c0ff71c..c71823cf 100644
--- a/pallas-traverse/src/block.rs
+++ b/pallas-traverse/src/block.rs
@@ -5,7 +5,8 @@ use pallas_crypto::hash::Hash;
use pallas_primitives::{alonzo, babbage, byron, conway};
use crate::{
- probe, support, Era, Error, MultiEraBlock, MultiEraHeader, MultiEraTx, MultiEraUpdate,
+ probe, support, Era, Error, MultiEraBlock, MultiEraBlockWithRawAuxiliary, MultiEraHeader,
+ MultiEraTx, MultiEraTxWithRawAuxiliary, MultiEraUpdate,
};
type BlockWrapper = (u16, T);
@@ -238,24 +239,259 @@ impl<'b> MultiEraBlock<'b> {
}
}
+impl<'b> MultiEraBlockWithRawAuxiliary<'b> {
+ pub fn decode_epoch_boundary(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::EpochBoundary(Box::new(block)))
+ }
+
+ pub fn decode_byron(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::Byron(Box::new(block)))
+ }
+
+ pub fn decode_shelley(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::AlonzoCompatible(Box::new(block), Era::Shelley))
+ }
+
+ pub fn decode_allegra(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::AlonzoCompatible(Box::new(block), Era::Allegra))
+ }
+
+ pub fn decode_mary(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::AlonzoCompatible(Box::new(block), Era::Mary))
+ }
+
+ pub fn decode_alonzo(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::AlonzoCompatible(Box::new(block), Era::Alonzo))
+ }
+
+ pub fn decode_babbage(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::Babbage(Box::new(block)))
+ }
+
+ pub fn decode_conway(cbor: &'b [u8]) -> Result {
+ let (_, block): BlockWrapper =
+ minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
+
+ Ok(Self::Conway(Box::new(block)))
+ }
+
+ pub fn decode(cbor: &'b [u8]) -> Result, Error> {
+ match probe::block_era(cbor) {
+ probe::Outcome::EpochBoundary => Self::decode_epoch_boundary(cbor),
+ probe::Outcome::Matched(era) => match era {
+ Era::Byron => Self::decode_byron(cbor),
+ Era::Shelley => Self::decode_shelley(cbor),
+ Era::Allegra => Self::decode_allegra(cbor),
+ Era::Mary => Self::decode_mary(cbor),
+ Era::Alonzo => Self::decode_alonzo(cbor),
+ Era::Babbage => Self::decode_babbage(cbor),
+ Era::Conway => Self::decode_conway(cbor),
+ },
+ probe::Outcome::Inconclusive => Err(Error::unknown_cbor(cbor)),
+ }
+ }
+
+ pub fn header(&self) -> MultiEraHeader<'_> {
+ match self {
+ Self::EpochBoundary(x) => MultiEraHeader::EpochBoundary(Cow::Borrowed(&x.header)),
+ Self::Byron(x) => MultiEraHeader::Byron(Cow::Borrowed(&x.header)),
+ Self::AlonzoCompatible(x, _) => {
+ MultiEraHeader::ShelleyCompatible(Cow::Borrowed(&x.header))
+ }
+ Self::Babbage(x) => MultiEraHeader::BabbageCompatible(Cow::Borrowed(&x.header)),
+ Self::Conway(x) => MultiEraHeader::BabbageCompatible(Cow::Borrowed(&x.header)),
+ }
+ }
+
+ /// Returns the block number (aka: height)
+ pub fn number(&self) -> u64 {
+ self.header().number()
+ }
+
+ pub fn era(&self) -> Era {
+ match self {
+ Self::EpochBoundary(_) => Era::Byron,
+ Self::AlonzoCompatible(_, x) => *x,
+ Self::Babbage(_) => Era::Babbage,
+ Self::Byron(_) => Era::Byron,
+ Self::Conway(_) => Era::Conway,
+ }
+ }
+
+ pub fn hash(&self) -> Hash<32> {
+ self.header().hash()
+ }
+
+ pub fn slot(&self) -> u64 {
+ self.header().slot()
+ }
+
+ /// Builds a vec with the Txs of the block
+ pub fn txs(&self) -> Vec {
+ match self {
+ Self::AlonzoCompatible(x, era) => support::clone_alonzo_txs_with_raw_aux(x)
+ .into_iter()
+ .map(|x| {
+ MultiEraTxWithRawAuxiliary::AlonzoCompatible(Box::new(Cow::Owned(x)), *era)
+ })
+ .collect(),
+ Self::Babbage(x) => support::clone_babbage_txs_with_raw_aux(x)
+ .into_iter()
+ .map(|x| MultiEraTxWithRawAuxiliary::Babbage(Box::new(Cow::Owned(x))))
+ .collect(),
+ Self::Byron(x) => support::clone_byron_txs(x)
+ .into_iter()
+ .map(|x| MultiEraTxWithRawAuxiliary::Byron(Box::new(Cow::Owned(x))))
+ .collect(),
+ Self::Conway(x) => support::clone_conway_txs_with_raw_aux(x)
+ .into_iter()
+ .map(|x| MultiEraTxWithRawAuxiliary::Conway(Box::new(Cow::Owned(x))))
+ .collect(),
+ Self::EpochBoundary(_) => vec![],
+ }
+ }
+
+ /// Returns true if the there're no tx in the block
+ pub fn is_empty(&self) -> bool {
+ match self {
+ Self::EpochBoundary(_) => true,
+ Self::AlonzoCompatible(x, _) => x.transaction_bodies.is_empty(),
+ Self::Babbage(x) => x.transaction_bodies.is_empty(),
+ Self::Byron(x) => x.body.tx_payload.is_empty(),
+ Self::Conway(x) => x.transaction_bodies.is_empty(),
+ }
+ }
+
+ /// Returns the count of txs in the block
+ pub fn tx_count(&self) -> usize {
+ match self {
+ Self::EpochBoundary(_) => 0,
+ Self::AlonzoCompatible(x, _) => x.transaction_bodies.len(),
+ Self::Babbage(x) => x.transaction_bodies.len(),
+ Self::Byron(x) => x.body.tx_payload.len(),
+ Self::Conway(x) => x.transaction_bodies.len(),
+ }
+ }
+
+ /// Returns true if the block has any auxiliary data
+ pub fn has_aux_data(&self) -> bool {
+ match self {
+ Self::EpochBoundary(_) => false,
+ Self::AlonzoCompatible(x, _) => !x.auxiliary_data_set.is_empty(),
+ Self::Babbage(x) => !x.auxiliary_data_set.is_empty(),
+ Self::Byron(_) => false,
+ Self::Conway(x) => !x.auxiliary_data_set.is_empty(),
+ }
+ }
+
+ /// Returns any block-level param update proposals (byron-specific)
+ pub fn update(&self) -> Option {
+ match self {
+ Self::Byron(x) => {
+ if let Some(up) = x.body.upd_payload.proposal.deref() {
+ // TODO: this might be horribly wrong, I'm assuming that the activation epoch
+ // for a Byron upgrade proposal is always current epoch + 1.
+ let epoch = x.header.consensus_data.0.epoch + 1;
+ Some(MultiEraUpdate::Byron(
+ epoch,
+ Box::new(Cow::Owned(up.clone())),
+ ))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+
+ pub fn as_alonzo(&self) -> Option<&alonzo::MintedBlockWithRawAuxiliary> {
+ match self {
+ Self::AlonzoCompatible(x, _) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_babbage(&self) -> Option<&babbage::MintedBlockWithRawAuxiliary> {
+ match self {
+ Self::Babbage(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_byron(&self) -> Option<&byron::MintedBlock> {
+ match self {
+ Self::Byron(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_conway(&self) -> Option<&conway::MintedBlockWithRawAuxiliary> {
+ match self {
+ Self::Conway(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ /// Return the size of the serialised block in bytes
+ pub fn size(&self) -> usize {
+ match self {
+ Self::EpochBoundary(b) => minicbor::to_vec(b).unwrap().len(),
+ Self::Byron(b) => minicbor::to_vec(b).unwrap().len(),
+ Self::AlonzoCompatible(b, _) => minicbor::to_vec(b).unwrap().len(),
+ Self::Babbage(b) => minicbor::to_vec(b).unwrap().len(),
+ Self::Conway(b) => minicbor::to_vec(b).unwrap().len(),
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
+ const TEST_BLOCKS: [(&'static str, usize); 5] = [
+ (include_str!("../../test_data/byron2.block"), 2usize),
+ (include_str!("../../test_data/shelley1.block"), 4),
+ (include_str!("../../test_data/mary1.block"), 14),
+ (include_str!("../../test_data/allegra1.block"), 3),
+ (include_str!("../../test_data/alonzo1.block"), 5),
+ ];
+
#[test]
fn test_iteration() {
- let blocks = vec![
- (include_str!("../../test_data/byron2.block"), 2usize),
- (include_str!("../../test_data/shelley1.block"), 4),
- (include_str!("../../test_data/mary1.block"), 14),
- (include_str!("../../test_data/allegra1.block"), 3),
- (include_str!("../../test_data/alonzo1.block"), 5),
- ];
-
- for (block_str, tx_count) in blocks.into_iter() {
+ for (block_str, tx_count) in TEST_BLOCKS.into_iter() {
let cbor = hex::decode(block_str).expect("invalid hex");
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
assert_eq!(block.txs().len(), tx_count);
}
}
+
+ #[test]
+ fn test_with_raw_aux_iteration() {
+ for (block_str, tx_count) in TEST_BLOCKS.into_iter() {
+ let cbor = hex::decode(block_str).expect("invalid hex");
+ let block = MultiEraBlockWithRawAuxiliary::decode(&cbor).expect("invalid cbor");
+ assert_eq!(block.txs().len(), tx_count);
+ }
+ }
}
diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs
index bce18563..7b05e20b 100644
--- a/pallas-traverse/src/lib.rs
+++ b/pallas-traverse/src/lib.rs
@@ -81,6 +81,16 @@ pub enum MultiEraBlock<'b> {
Conway(Box>),
}
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum MultiEraBlockWithRawAuxiliary<'b> {
+ EpochBoundary(Box>),
+ AlonzoCompatible(Box>, Era),
+ Babbage(Box>),
+ Byron(Box>),
+ Conway(Box>),
+}
+
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraTx<'b> {
@@ -90,6 +100,15 @@ pub enum MultiEraTx<'b> {
Conway(Box>>),
}
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum MultiEraTxWithRawAuxiliary<'b> {
+ AlonzoCompatible(Box>>, Era),
+ Babbage(Box>>),
+ Byron(Box>>),
+ Conway(Box>>),
+}
+
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraValue<'b> {
diff --git a/pallas-traverse/src/size.rs b/pallas-traverse/src/size.rs
index b978d1e5..efbebbdf 100644
--- a/pallas-traverse/src/size.rs
+++ b/pallas-traverse/src/size.rs
@@ -1,6 +1,6 @@
use pallas_codec::utils::Nullable;
-use crate::{MultiEraBlock, MultiEraTx};
+use crate::{MultiEraBlock, MultiEraBlockWithRawAuxiliary, MultiEraTx, MultiEraTxWithRawAuxiliary};
impl<'b> MultiEraTx<'b> {
fn aux_data_size(&self) -> usize {
@@ -44,6 +44,48 @@ impl<'b> MultiEraTx<'b> {
}
}
+impl<'b> MultiEraTxWithRawAuxiliary<'b> {
+ fn aux_data_size(&self) -> usize {
+ match self {
+ Self::AlonzoCompatible(x, _) => match &x.auxiliary_data {
+ Nullable::Some(x) => x.raw_cbor().len(),
+ _ => 2,
+ },
+ Self::Babbage(x) => match &x.auxiliary_data {
+ Nullable::Some(x) => x.raw_cbor().len(),
+ _ => 2,
+ },
+ Self::Byron(_) => 0,
+ Self::Conway(x) => match &x.auxiliary_data {
+ Nullable::Some(x) => x.raw_cbor().len(),
+ _ => 2,
+ },
+ }
+ }
+
+ fn body_size(&self) -> usize {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.raw_cbor().len(),
+ Self::Babbage(x) => x.transaction_body.raw_cbor().len(),
+ Self::Byron(x) => x.transaction.raw_cbor().len(),
+ Self::Conway(x) => x.transaction_body.raw_cbor().len(),
+ }
+ }
+
+ fn witness_set_size(&self) -> usize {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_witness_set.raw_cbor().len(),
+ Self::Babbage(x) => x.transaction_witness_set.raw_cbor().len(),
+ Self::Byron(x) => x.witness.raw_cbor().len(),
+ Self::Conway(x) => x.transaction_witness_set.raw_cbor().len(),
+ }
+ }
+
+ pub fn size(&self) -> usize {
+ self.body_size() + self.witness_set_size() + self.aux_data_size()
+ }
+}
+
impl<'b> MultiEraBlock<'b> {
pub fn body_size(&self) -> Option {
match self {
@@ -57,3 +99,15 @@ impl<'b> MultiEraBlock<'b> {
}
}
}
+
+impl<'b> MultiEraBlockWithRawAuxiliary<'b> {
+ pub fn body_size(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => Some(x.header.header_body.block_body_size as usize),
+ Self::Babbage(x) => Some(x.header.header_body.block_body_size as usize),
+ Self::EpochBoundary(_) => None,
+ Self::Byron(_) => None,
+ Self::Conway(x) => Some(x.header.header_body.block_body_size as usize),
+ }
+ }
+}
diff --git a/pallas-traverse/src/support.rs b/pallas-traverse/src/support.rs
index 8465a3ec..4a8e8f00 100644
--- a/pallas-traverse/src/support.rs
+++ b/pallas-traverse/src/support.rs
@@ -3,8 +3,8 @@
use pallas_primitives::{alonzo, babbage, byron, conway};
macro_rules! clone_tx_fn {
- ($fn_name:ident, $era:tt) => {
- fn $fn_name<'b>(block: &'b $era::MintedBlock, index: usize) -> Option<$era::MintedTx<'b>> {
+ ($fn_name:ident, $era:tt, $block_type:ident, $tx_type:ident) => {
+ fn $fn_name<'b>(block: &'b $era::$block_type, index: usize) -> Option<$era::$tx_type<'b>> {
let transaction_body = block.transaction_bodies.get(index).cloned()?;
let transaction_witness_set = block.transaction_witness_sets.get(index)?.clone();
@@ -28,7 +28,7 @@ macro_rules! clone_tx_fn {
.cloned()
.into();
- let x = $era::MintedTx {
+ let x = $era::$tx_type {
transaction_body,
transaction_witness_set,
success,
@@ -40,9 +40,27 @@ macro_rules! clone_tx_fn {
};
}
-clone_tx_fn!(conway_clone_tx_at, conway);
-clone_tx_fn!(babbage_clone_tx_at, babbage);
-clone_tx_fn!(alonzo_clone_tx_at, alonzo);
+clone_tx_fn!(conway_clone_tx_at, conway, MintedBlock, MintedTx);
+clone_tx_fn!(
+ conway_clone_tx_with_raw_aux_at,
+ conway,
+ MintedBlockWithRawAuxiliary,
+ MintedTxWithRawAuxiliary
+);
+clone_tx_fn!(babbage_clone_tx_at, babbage, MintedBlock, MintedTx);
+clone_tx_fn!(
+ babbage_clone_tx_with_raw_aux_at,
+ babbage,
+ MintedBlockWithRawAuxiliary,
+ MintedTxWithRawAuxiliary
+);
+clone_tx_fn!(alonzo_clone_tx_at, alonzo, MintedBlock, MintedTx);
+clone_tx_fn!(
+ alonzo_clone_tx_with_raw_aux_at,
+ alonzo,
+ MintedBlockWithRawAuxiliary,
+ MintedTxWithRawAuxiliary
+);
pub fn clone_alonzo_txs<'b>(block: &'b alonzo::MintedBlock) -> Vec> {
(0..block.transaction_bodies.len())
@@ -51,6 +69,15 @@ pub fn clone_alonzo_txs<'b>(block: &'b alonzo::MintedBlock) -> Vec(
+ block: &'b alonzo::MintedBlockWithRawAuxiliary,
+) -> Vec> {
+ (0..block.transaction_bodies.len())
+ .step_by(1)
+ .filter_map(|idx| alonzo_clone_tx_with_raw_aux_at(block, idx))
+ .collect()
+}
+
pub fn clone_babbage_txs<'b>(block: &'b babbage::MintedBlock) -> Vec> {
(0..block.transaction_bodies.len())
.step_by(1)
@@ -58,6 +85,15 @@ pub fn clone_babbage_txs<'b>(block: &'b babbage::MintedBlock) -> Vec(
+ block: &'b babbage::MintedBlockWithRawAuxiliary,
+) -> Vec> {
+ (0..block.transaction_bodies.len())
+ .step_by(1)
+ .filter_map(|idx| babbage_clone_tx_with_raw_aux_at(block, idx))
+ .collect()
+}
+
pub fn clone_conway_txs<'b>(block: &'b conway::MintedBlock) -> Vec> {
(0..block.transaction_bodies.len())
.step_by(1)
@@ -65,6 +101,15 @@ pub fn clone_conway_txs<'b>(block: &'b conway::MintedBlock) -> Vec(
+ block: &'b conway::MintedBlockWithRawAuxiliary,
+) -> Vec> {
+ (0..block.transaction_bodies.len())
+ .step_by(1)
+ .filter_map(|idx| conway_clone_tx_with_raw_aux_at(block, idx))
+ .collect()
+}
+
pub fn clone_byron_txs<'b>(block: &'b byron::MintedBlock) -> Vec> {
block.body.tx_payload.iter().cloned().collect()
}
diff --git a/pallas-traverse/src/time.rs b/pallas-traverse/src/time.rs
index a5b8c0c7..62b7845d 100644
--- a/pallas-traverse/src/time.rs
+++ b/pallas-traverse/src/time.rs
@@ -1,4 +1,4 @@
-use crate::{wellknown::GenesisValues, MultiEraBlock};
+use crate::{wellknown::GenesisValues, MultiEraBlock, MultiEraBlockWithRawAuxiliary};
pub type Epoch = u64;
@@ -144,6 +144,29 @@ impl<'a> MultiEraBlock<'a> {
}
}
+impl<'a> MultiEraBlockWithRawAuxiliary<'a> {
+ pub fn epoch(&self, genesis: &GenesisValues) -> (Epoch, SubSlot) {
+ match self {
+ Self::EpochBoundary(x) => (x.header.consensus_data.epoch_id, 0),
+ Self::Byron(x) => (
+ x.header.consensus_data.0.epoch,
+ x.header.consensus_data.0.slot,
+ ),
+ Self::AlonzoCompatible(x, _) => {
+ genesis.absolute_slot_to_relative(x.header.header_body.slot)
+ }
+ Self::Babbage(x) => genesis.absolute_slot_to_relative(x.header.header_body.slot),
+ Self::Conway(x) => genesis.absolute_slot_to_relative(x.header.header_body.slot),
+ }
+ }
+
+ /// Computes the unix timestamp for the slot of the tx
+ pub fn wallclock(&self, genesis: &GenesisValues) -> u64 {
+ let slot = self.slot();
+ genesis.slot_to_wallclock(slot)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -270,5 +293,18 @@ mod tests {
);
assert_eq!(computed_slot, 4492794);
+
+ let block = MultiEraBlockWithRawAuxiliary::decode(&block_cbor).expect("invalid cbor");
+
+ let byron = block.as_byron().unwrap();
+
+ let genesis = GenesisValues::default();
+
+ let computed_slot = genesis.relative_slot_to_absolute(
+ byron.header.consensus_data.0.epoch,
+ byron.header.consensus_data.0.slot,
+ );
+
+ assert_eq!(computed_slot, 4492794);
}
}
diff --git a/pallas-traverse/src/tx.rs b/pallas-traverse/src/tx.rs
index 21b1db40..ee420555 100644
--- a/pallas-traverse/src/tx.rs
+++ b/pallas-traverse/src/tx.rs
@@ -6,12 +6,13 @@ use pallas_crypto::hash::Hash;
use pallas_primitives::{
alonzo,
babbage::{self, NetworkId},
- byron, conway,
+ byron, conway, OnlyRaw,
};
use crate::{
Era, Error, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraOutput, MultiEraPolicyAssets,
- MultiEraSigners, MultiEraTx, MultiEraUpdate, MultiEraWithdrawals, OriginalHash,
+ MultiEraSigners, MultiEraTx, MultiEraTxWithRawAuxiliary, MultiEraUpdate, MultiEraWithdrawals,
+ OriginalHash,
};
impl<'b> MultiEraTx<'b> {
@@ -625,3 +626,598 @@ impl<'b> MultiEraTx<'b> {
}
}
}
+
+impl<'b> MultiEraTxWithRawAuxiliary<'b> {
+ pub fn from_byron(tx: &'b byron::MintedTxPayload<'b>) -> Self {
+ Self::Byron(Box::new(Cow::Borrowed(tx)))
+ }
+
+ pub fn from_alonzo_compatible(tx: &'b alonzo::MintedTxWithRawAuxiliary<'b>, era: Era) -> Self {
+ Self::AlonzoCompatible(Box::new(Cow::Borrowed(tx)), era)
+ }
+
+ pub fn from_babbage(tx: &'b babbage::MintedTxWithRawAuxiliary<'b>) -> Self {
+ Self::Babbage(Box::new(Cow::Borrowed(tx)))
+ }
+
+ pub fn encode(&self) -> Vec {
+ // to_vec is infallible
+ match self {
+ Self::AlonzoCompatible(x, _) => minicbor::to_vec(x).unwrap(),
+ Self::Babbage(x) => minicbor::to_vec(x).unwrap(),
+ Self::Byron(x) => minicbor::to_vec(x).unwrap(),
+ Self::Conway(x) => minicbor::to_vec(x).unwrap(),
+ }
+ }
+
+ pub fn decode_for_era(era: Era, cbor: &'b [u8]) -> Result {
+ match era {
+ Era::Byron => {
+ let tx = minicbor::decode(cbor)?;
+ let tx = Box::new(Cow::Owned(tx));
+ Ok(Self::Byron(tx))
+ }
+ Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => {
+ let tx = minicbor::decode(cbor)?;
+ let tx = Box::new(Cow::Owned(tx));
+ Ok(Self::AlonzoCompatible(tx, era))
+ }
+ Era::Babbage => {
+ let tx = minicbor::decode(cbor)?;
+ let tx = Box::new(Cow::Owned(tx));
+ Ok(Self::Babbage(tx))
+ }
+ Era::Conway => {
+ let tx = minicbor::decode(cbor)?;
+ let tx = Box::new(Cow::Owned(tx));
+ Ok(Self::Conway(tx))
+ }
+ }
+ }
+
+ /// Try decode a transaction via every era's encoding format, starting with
+ /// the most recent and returning on first success, or None if none are
+ /// successful
+ ///
+ /// NOTE: Until Conway is officially released, this method favors Babbage
+ /// decoding over Conway decoding. This means that we'll attempt to
+ /// decode using Babbage first even if Conway is newer.
+ pub fn decode(cbor: &'b [u8]) -> Result {
+ if let Ok(tx) = minicbor::decode(cbor) {
+ return Ok(Self::Conway(Box::new(Cow::Owned(tx))));
+ }
+
+ if let Ok(tx) = minicbor::decode(cbor) {
+ return Ok(Self::Babbage(Box::new(Cow::Owned(tx))));
+ }
+
+ if let Ok(tx) = minicbor::decode(cbor) {
+ // Shelley/Allegra/Mary/Alonzo will all decode to Alonzo
+ return Ok(Self::AlonzoCompatible(
+ Box::new(Cow::Owned(tx)),
+ Era::Alonzo,
+ ));
+ }
+
+ if let Ok(tx) = minicbor::decode(cbor) {
+ Ok(Self::Byron(Box::new(Cow::Owned(tx))))
+ } else {
+ Err(Error::unknown_cbor(cbor))
+ }
+ }
+
+ pub fn era(&self) -> Era {
+ match self {
+ Self::AlonzoCompatible(_, era) => *era,
+ Self::Babbage(_) => Era::Babbage,
+ Self::Byron(_) => Era::Byron,
+ Self::Conway(_) => Era::Conway,
+ }
+ }
+
+ pub fn hash(&self) -> Hash<32> {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.original_hash(),
+ Self::Babbage(x) => x.transaction_body.original_hash(),
+ Self::Byron(x) => x.transaction.original_hash(),
+ Self::Conway(x) => x.transaction_body.original_hash(),
+ }
+ }
+
+ pub fn outputs(&self) -> Vec {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .outputs
+ .iter()
+ .map(|x| MultiEraOutput::from_alonzo_compatible(x, self.era()))
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .outputs
+ .iter()
+ .map(MultiEraOutput::from_babbage)
+ .collect(),
+ Self::Byron(x) => x
+ .transaction
+ .outputs
+ .iter()
+ .map(MultiEraOutput::from_byron)
+ .collect(),
+ Self::Conway(x) => x
+ .transaction_body
+ .outputs
+ .iter()
+ .map(MultiEraOutput::from_conway)
+ .collect(),
+ }
+ }
+
+ pub fn output_at(&self, index: usize) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .outputs
+ .get(index)
+ .map(|x| MultiEraOutput::from_alonzo_compatible(x, self.era())),
+ Self::Babbage(x) => x
+ .transaction_body
+ .outputs
+ .get(index)
+ .map(MultiEraOutput::from_babbage),
+ Self::Byron(x) => x
+ .transaction
+ .outputs
+ .get(index)
+ .map(MultiEraOutput::from_byron),
+ Self::Conway(x) => x
+ .transaction_body
+ .outputs
+ .get(index)
+ .map(MultiEraOutput::from_conway),
+ }
+ }
+
+ /// Return the transaction inputs
+ ///
+ /// NOTE: It is possible for this to return duplicates before some point in the chain history. See https://github.com/input-output-hk/cardano-ledger/commit/a342b74f5db3d3a75eae3e2abe358a169701b1e7
+ pub fn inputs(&self) -> Vec {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .inputs
+ .iter()
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .inputs
+ .iter()
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ Self::Byron(x) => x
+ .transaction
+ .inputs
+ .iter()
+ .map(MultiEraInput::from_byron)
+ .collect(),
+ Self::Conway(x) => x
+ .transaction_body
+ .inputs
+ .iter()
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ }
+ }
+
+ /// Return inputs as expected for processing
+ ///
+ /// To process inputs we need a set (no duplicated) and lexicographical
+ /// order (hash#idx). This function will take the raw inputs and apply the
+ /// aforementioned cleanup changes.
+ pub fn inputs_sorted_set(&self) -> Vec {
+ let mut raw = self.inputs();
+ raw.sort_by_key(|x| x.lexicographical_key());
+ raw.dedup_by_key(|x| x.lexicographical_key());
+
+ raw
+ }
+
+ pub fn mints_sorted_set(&self) -> Vec {
+ let mut raw = self.mints();
+
+ raw.sort_by_key(|m| *m.policy());
+
+ raw
+ }
+
+ pub fn withdrawals_sorted_set(&self) -> Vec<(&[u8], u64)> {
+ match self.withdrawals() {
+ MultiEraWithdrawals::NotApplicable | MultiEraWithdrawals::Empty => {
+ std::iter::empty().collect()
+ }
+ MultiEraWithdrawals::AlonzoCompatible(x) => x
+ .iter()
+ .map(|(k, v)| (k.as_slice(), *v))
+ .sorted_by_key(|(k, _)| *k)
+ .collect(),
+ MultiEraWithdrawals::Conway(x) => x
+ .iter()
+ .map(|(k, v)| (k.as_slice(), *v))
+ .sorted_by_key(|(k, _)| *k)
+ .collect(),
+ }
+ }
+
+ /// Return the transaction reference inputs
+ ///
+ /// NOTE: It is possible for this to return duplicates. See
+ /// https://github.com/input-output-hk/cardano-ledger/commit/a342b74f5db3d3a75eae3e2abe358a169701b1e7
+ pub fn reference_inputs(&self) -> Vec {
+ match self {
+ Self::Conway(x) => x
+ .transaction_body
+ .reference_inputs
+ .iter()
+ .flatten()
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .reference_inputs
+ .iter()
+ .flatten()
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ _ => vec![],
+ }
+ }
+
+ pub fn certs(&self) -> Vec {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .certificates
+ .iter()
+ .flat_map(|c| c.iter())
+ .map(|c| MultiEraCert::AlonzoCompatible(Box::new(Cow::Borrowed(c))))
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .certificates
+ .iter()
+ .flat_map(|c| c.iter())
+ .map(|c| MultiEraCert::AlonzoCompatible(Box::new(Cow::Borrowed(c))))
+ .collect(),
+ Self::Byron(_) => vec![],
+ Self::Conway(x) => x
+ .transaction_body
+ .certificates
+ .iter()
+ .flat_map(|c| c.iter())
+ .map(|c| MultiEraCert::Conway(Box::new(Cow::Borrowed(c))))
+ .collect(),
+ }
+ }
+
+ pub fn update(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .update
+ .as_ref()
+ .map(MultiEraUpdate::from_alonzo_compatible),
+ Self::Babbage(x) => x
+ .transaction_body
+ .update
+ .as_ref()
+ .map(MultiEraUpdate::from_babbage),
+ Self::Byron(_) => None,
+ Self::Conway(_) => None,
+ }
+ }
+
+ pub fn mints(&self) -> Vec {
+ match self {
+ Self::Byron(_) => vec![],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .mint
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleMint(k, v))
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .mint
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleMint(k, v))
+ .collect(),
+ Self::Conway(x) => x
+ .transaction_body
+ .mint
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(|(k, v)| MultiEraPolicyAssets::ConwayMint(k, v))
+ .collect(),
+ }
+ }
+
+ /// Return the transaction collateral inputs
+ ///
+ /// NOTE: It is possible for this to return duplicates. See
+ /// https://github.com/input-output-hk/cardano-ledger/commit/a342b74f5db3d3a75eae3e2abe358a169701b1e7
+ pub fn collateral(&self) -> Vec {
+ match self {
+ Self::Byron(_) => vec![],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .collateral
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .collateral
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ Self::Conway(x) => x
+ .transaction_body
+ .collateral
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(MultiEraInput::from_alonzo_compatible)
+ .collect(),
+ }
+ }
+
+ pub fn collateral_return(&self) -> Option {
+ match self {
+ Self::Babbage(x) => x
+ .transaction_body
+ .collateral_return
+ .as_ref()
+ .map(MultiEraOutput::from_babbage),
+ Self::Conway(x) => x
+ .transaction_body
+ .collateral_return
+ .as_ref()
+ .map(MultiEraOutput::from_conway),
+ _ => None,
+ }
+ }
+
+ pub fn total_collateral(&self) -> Option {
+ match self {
+ Self::Babbage(x) => x.transaction_body.total_collateral,
+ Self::Conway(x) => x.transaction_body.total_collateral,
+ _ => None,
+ }
+ }
+
+ /// Returns the list of inputs consumed by the Tx
+ ///
+ /// Helper method to abstract the logic of which inputs are consumed
+ /// depending on the validity of the Tx. If the Tx is valid, this method
+ /// will return the list of inputs. If the tx is invalid, it will return the
+ /// collateral.
+ pub fn consumes(&self) -> Vec {
+ let consumed = match self.is_valid() {
+ true => self.inputs(),
+ false => self.collateral(),
+ };
+
+ let mut unique_consumed = HashSet::new();
+
+ consumed
+ .into_iter()
+ .filter(|i| unique_consumed.insert(i.output_ref()))
+ .collect()
+ }
+
+ /// Returns a list of tuples of the outputs produced by the Tx with their
+ /// indexes
+ ///
+ /// Helper method to abstract the logic of which outputs are produced
+ /// depending on the validity of the Tx. If the Tx is valid, this method
+ /// will return the list of outputs. If the Tx is invalid it will return the
+ /// collateral return if one is present or an empty list if not. Note that
+ /// the collateral return output index is defined as the next available
+ /// index after the txouts (Babbage spec, ch 4).
+ pub fn produces(&self) -> Vec<(usize, MultiEraOutput)> {
+ match self.is_valid() {
+ true => self.outputs().into_iter().enumerate().collect(),
+ false => self
+ .collateral_return()
+ .into_iter()
+ .map(|txo| (self.outputs().len(), txo))
+ .collect(),
+ }
+ }
+
+ /// Returns the *produced* output at the given index if one exists
+ ///
+ /// If the transaction is valid the outputs are produced, otherwise the
+ /// collateral return output is produced at index |outputs.len()| if one is
+ /// present. This function gets the *produced* output for an index if one
+ /// exists. It behaves exactly as `outputs_at` for valid transactions, but
+ /// for invalid transactions it returns None except for if the index points
+ /// to the collateral-return output and one is present in the transaction,
+ /// in which case it returns the collateral-return output.
+ pub fn produces_at(&self, index: usize) -> Option {
+ match self.is_valid() {
+ true => self.output_at(index),
+ false => {
+ if index == self.outputs().len() {
+ self.collateral_return()
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ /// Returns the list of UTxO required by the Tx
+ ///
+ /// Helper method to yield all of the UTxO that the Tx requires in order to
+ /// be fulfilled. This includes normal inputs, reference inputs and
+ /// collateral.
+ pub fn requires(&self) -> Vec {
+ [self.inputs(), self.reference_inputs(), self.collateral()].concat()
+ }
+
+ pub fn withdrawals(&self) -> MultiEraWithdrawals {
+ match self {
+ Self::AlonzoCompatible(x, _) => match &x.transaction_body.withdrawals {
+ Some(x) => MultiEraWithdrawals::AlonzoCompatible(x),
+ None => MultiEraWithdrawals::Empty,
+ },
+ Self::Babbage(x) => match &x.transaction_body.withdrawals {
+ Some(x) => MultiEraWithdrawals::AlonzoCompatible(x),
+ None => MultiEraWithdrawals::Empty,
+ },
+ Self::Byron(_) => MultiEraWithdrawals::NotApplicable,
+ Self::Conway(x) => match &x.transaction_body.withdrawals {
+ Some(x) => MultiEraWithdrawals::Conway(x),
+ None => MultiEraWithdrawals::Empty,
+ },
+ }
+ }
+
+ pub fn fee(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => Some(x.transaction_body.fee),
+ Self::Babbage(x) => Some(x.transaction_body.fee),
+ Self::Byron(_) => None,
+ Self::Conway(x) => Some(x.transaction_body.fee),
+ }
+ }
+
+ pub fn ttl(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.ttl,
+ Self::Babbage(x) => x.transaction_body.ttl,
+ Self::Byron(_) => None,
+ Self::Conway(x) => x.transaction_body.ttl,
+ }
+ }
+
+ /// Returns the fee or attempts to compute it
+ ///
+ /// If the fee is available as part of the tx data (post-byron), this
+ /// function will return the existing value. For byron txs, this method
+ /// attempts to compute the value by using the linear fee policy.
+ #[cfg(feature = "unstable")]
+ pub fn fee_or_compute(&self) -> u64 {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.fee,
+ Self::Babbage(x) => x.transaction_body.fee,
+ Self::Byron(x) => crate::fees::compute_byron_fee(x, None),
+ Self::Conway(x) => x.transaction_body.fee,
+ }
+ }
+
+ pub fn aux_data(&self) -> Option<&OnlyRaw<'_, alonzo::AuxiliaryData>> {
+ match self {
+ Self::AlonzoCompatible(x, _) => match &x.auxiliary_data {
+ pallas_codec::utils::Nullable::Some(x) => Some(x),
+ pallas_codec::utils::Nullable::Null => None,
+ pallas_codec::utils::Nullable::Undefined => None,
+ },
+ Self::Babbage(x) => match &x.auxiliary_data {
+ pallas_codec::utils::Nullable::Some(x) => Some(x),
+ pallas_codec::utils::Nullable::Null => None,
+ pallas_codec::utils::Nullable::Undefined => None,
+ },
+ Self::Byron(_) => None,
+ Self::Conway(x) => match &x.auxiliary_data {
+ pallas_codec::utils::Nullable::Some(x) => Some(x),
+ pallas_codec::utils::Nullable::Null => None,
+ pallas_codec::utils::Nullable::Undefined => None,
+ },
+ }
+ }
+
+ pub fn required_signers(&self) -> MultiEraSigners {
+ match self {
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_body
+ .required_signers
+ .as_ref()
+ .map(MultiEraSigners::AlonzoCompatible)
+ .unwrap_or_default(),
+ Self::Babbage(x) => x
+ .transaction_body
+ .required_signers
+ .as_ref()
+ .map(MultiEraSigners::AlonzoCompatible)
+ .unwrap_or_default(),
+ Self::Byron(_) => MultiEraSigners::NotApplicable,
+ Self::Conway(x) => x
+ .transaction_body
+ .required_signers
+ .as_ref()
+ .map(|x| MultiEraSigners::AlonzoCompatible(x.deref()))
+ .unwrap_or_default(),
+ }
+ }
+
+ pub fn validity_start(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.validity_interval_start,
+ Self::Babbage(x) => x.transaction_body.validity_interval_start,
+ Self::Byron(_) => None,
+ Self::Conway(x) => x.transaction_body.validity_interval_start,
+ }
+ }
+
+ pub fn network_id(&self) -> Option {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.transaction_body.network_id,
+ Self::Babbage(x) => x.transaction_body.network_id,
+ Self::Byron(_) => None,
+ Self::Conway(x) => x.transaction_body.network_id,
+ }
+ }
+
+ pub fn is_valid(&self) -> bool {
+ match self {
+ Self::AlonzoCompatible(x, _) => x.success,
+ Self::Babbage(x) => x.success,
+ Self::Byron(_) => true,
+ Self::Conway(x) => x.success,
+ }
+ }
+
+ pub fn as_babbage(&self) -> Option<&babbage::MintedTxWithRawAuxiliary> {
+ match self {
+ Self::Babbage(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_alonzo(&self) -> Option<&alonzo::MintedTxWithRawAuxiliary> {
+ match self {
+ Self::AlonzoCompatible(x, _) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_byron(&self) -> Option<&byron::MintedTxPayload> {
+ match self {
+ Self::Byron(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_conway(&self) -> Option<&conway::MintedTxWithRawAuxiliary> {
+ match self {
+ Self::Conway(x) => Some(x),
+ _ => None,
+ }
+ }
+}
diff --git a/pallas-traverse/src/witnesses.rs b/pallas-traverse/src/witnesses.rs
index 9c8cf3e7..ee36256a 100644
--- a/pallas-traverse/src/witnesses.rs
+++ b/pallas-traverse/src/witnesses.rs
@@ -4,7 +4,7 @@ use pallas_primitives::{
conway, PlutusData, PlutusScript,
};
-use crate::{MultiEraRedeemer, MultiEraTx};
+use crate::{MultiEraRedeemer, MultiEraTx, MultiEraTxWithRawAuxiliary};
impl<'b> MultiEraTx<'b> {
pub fn vkey_witnesses(&self) -> &[VKeyWitness] {
@@ -217,3 +217,215 @@ impl<'b> MultiEraTx<'b> {
}
}
}
+
+impl<'b> MultiEraTxWithRawAuxiliary<'b> {
+ pub fn vkey_witnesses(&self) -> &[VKeyWitness] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .vkeywitness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .vkeywitness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .vkeywitness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn native_scripts(&self) -> &[KeepRaw<'b, NativeScript>] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .native_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .native_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .native_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn bootstrap_witnesses(&self) -> &[BootstrapWitness] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .bootstrap_witness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .bootstrap_witness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .bootstrap_witness
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn plutus_v1_scripts(&self) -> &[alonzo::PlutusScript<1>] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .plutus_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .plutus_v1_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .plutus_v1_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn plutus_data(&self) -> &[KeepRaw<'b, PlutusData>] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .plutus_data
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .plutus_data
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .plutus_data
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn redeemers(&self) -> Vec {
+ match self {
+ Self::Byron(_) => vec![],
+ Self::AlonzoCompatible(x, _) => x
+ .transaction_witness_set
+ .redeemer
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(MultiEraRedeemer::from_alonzo_compatible)
+ .collect(),
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .redeemer
+ .iter()
+ .flat_map(|x| x.iter())
+ .map(MultiEraRedeemer::from_alonzo_compatible)
+ .collect(),
+ Self::Conway(x) => match x.transaction_witness_set.redeemer.as_deref() {
+ Some(conway::Redeemers::Map(x)) => x
+ .iter()
+ .map(|(k, v)| MultiEraRedeemer::from_conway(k, v))
+ .collect(),
+ Some(conway::Redeemers::List(x)) => x
+ .iter()
+ .map(MultiEraRedeemer::from_conway_deprecated)
+ .collect(),
+ _ => vec![],
+ },
+ }
+ }
+
+ pub fn find_spend_redeemer(&self, input_order: u32) -> Option {
+ self.redeemers().into_iter().find(|r| {
+ r.tag() == pallas_primitives::conway::RedeemerTag::Spend && r.index() == input_order
+ })
+ }
+
+ pub fn find_mint_redeemer(&self, mint_order: u32) -> Option {
+ self.redeemers().into_iter().find(|r| {
+ r.tag() == pallas_primitives::conway::RedeemerTag::Mint && r.index() == mint_order
+ })
+ }
+
+ pub fn find_withdrawal_redeemer(&self, withdrawal_order: u32) -> Option {
+ self.redeemers().into_iter().find(|r| {
+ r.tag() == pallas_primitives::conway::RedeemerTag::Reward
+ && r.index() == withdrawal_order
+ })
+ }
+
+ pub fn find_certificate_redeemer(&self, certificate_order: u32) -> Option {
+ self.redeemers().into_iter().find(|r| {
+ r.tag() == pallas_primitives::conway::RedeemerTag::Cert
+ && r.index() == certificate_order
+ })
+ }
+
+ pub fn plutus_v2_scripts(&self) -> &[PlutusScript<2>] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(_, _) => &[],
+ Self::Babbage(x) => x
+ .transaction_witness_set
+ .plutus_v2_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .plutus_v2_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+
+ pub fn plutus_v3_scripts(&self) -> &[PlutusScript<3>] {
+ match self {
+ Self::Byron(_) => &[],
+ Self::AlonzoCompatible(_, _) => &[],
+ Self::Babbage(_) => &[],
+ Self::Conway(x) => x
+ .transaction_witness_set
+ .plutus_v3_script
+ .as_ref()
+ .map(|x| x.as_ref())
+ .unwrap_or(&[]),
+ }
+ }
+}