feat: Introduce 'traverse' library (#117)

This commit is contained in:
Santiago Carmuega 2022-06-12 19:09:33 -03:00 committed by GitHub
parent fe80ff7800
commit 26da913ad9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 377 additions and 106 deletions

View file

@ -6,6 +6,7 @@ members = [
"pallas-miniprotocols",
"pallas-crypto",
"pallas-primitives",
"pallas-traverse",
"pallas",
"examples/block-download",
"examples/block-decode",

View file

@ -42,6 +42,7 @@ As already explained, _Pallas_ aims at being an expanding set of components. The
| Crates | Description |
| --------------------------------------- | ----------------------------------------------------------------------- |
| [pallas-primitives](/pallas-primitives) | Ledger primitives and cbor codec for the different Cardano eras |
| [pallas-traverse](/pallas-traverse) | Utilities to traverse over multi-era block data |
| pallas-ticking | Time passage implementation for consensus algorithm |
| pallas-applying | Logic for validating and applying new blocks and txs to the chain state |
| pallas-forecasting | Ledger forecasting algorithm to be used by the consensus layer |

View file

@ -1,4 +1,4 @@
use pallas::ledger::primitives::{alonzo, byron, probing, Era};
use pallas::ledger::traverse::MultiEraBlock;
fn main() {
let blocks = vec![
@ -10,23 +10,14 @@ fn main() {
];
for block_str in blocks.iter() {
let bytes = hex::decode(block_str).expect("invalid hex");
let cbor = hex::decode(block_str).expect("invalid hex");
match probing::probe_block_cbor_era(&bytes) {
probing::Outcome::Matched(era) => match era {
Era::Byron => {
let (_, block): (u16, byron::MainBlock) =
pallas::codec::minicbor::decode(&bytes).expect("invalid cbor");
println!("{:?}", block)
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
println!("{} {}", block.slot(), block.hash());
for tx in block.tx_iter() {
println!("{:?}", tx);
}
// we use alonzo for everything post-shelly since it's backward compatible
Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => {
let (_, block): (u16, alonzo::Block) =
pallas::codec::minicbor::decode(&bytes).expect("invalid cbor");
println!("{:?}", block)
}
},
_ => println!("couldn't infer block era"),
};
}
}

View file

@ -111,7 +111,7 @@ where
}
/// A struct that maintains a reference to whether a cbor array was indef or not
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum MaybeIndefArray<A> {
Def(Vec<A>),
Indef(Vec<A>),
@ -186,7 +186,7 @@ where
/// transform key-value structures into an orderer vec of `properties`, where
/// each entry represents a a cbor-encodable variant of an attribute of the
/// struct.
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub struct OrderPreservingProperties<P>(Vec<P>);
impl<P> Deref for OrderPreservingProperties<P> {
@ -229,7 +229,7 @@ where
}
/// Wraps a struct so that it is encoded/decoded as a cbor bytes
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CborWrap<T>(pub T);
impl<'b, C, T> minicbor::Decode<'b, C> for CborWrap<T>
@ -312,7 +312,7 @@ where
/// An empty map
///
/// don't ask me why, that's what the CDDL asks for.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct EmptyMap;
impl<'b, C> minicbor::decode::Decode<'b, C> for EmptyMap {
@ -518,7 +518,7 @@ impl From<&AnyUInt> for u64 {
/// let confirm: (u16, u16) = minicbor::decode(keeper.raw_cbor()).unwrap();
/// assert_eq!(confirm, (456u16, 789u16));
/// ```
#[derive(Debug, PartialEq, PartialOrd)]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct KeepRaw<'b, T> {
raw: &'b [u8],
inner: T,

View file

@ -74,7 +74,7 @@ pub struct Header {
pub body_signature: ByteVec,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct TransactionInput {
#[n(0)]
pub transaction_id: Hash<32>,
@ -85,7 +85,7 @@ pub struct TransactionInput {
// $nonce /= [ 0 // 1, bytes .size 32 ]
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(index_only)]
pub enum NonceVariant {
#[n(0)]
@ -95,7 +95,7 @@ pub enum NonceVariant {
Nonce,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct Nonce {
#[n(0)]
pub variant: NonceVariant,
@ -162,7 +162,7 @@ impl<C> minicbor::encode::Encode<C> for Value {
}
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct TransactionOutput {
#[n(0)]
pub address: ByteVec,
@ -187,7 +187,7 @@ pub type VrfKeyhash = Hash<32>;
; otherwise the funds are given to the other accounting pot.
*/
#[derive(Debug, PartialEq, PartialOrd)]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum InstantaneousRewardSource {
Reserves,
Treasury,
@ -225,7 +225,7 @@ impl<C> minicbor::encode::Encode<C> for InstantaneousRewardSource {
}
}
#[derive(Debug, PartialEq, PartialOrd)]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum InstantaneousRewardTarget {
StakeCredentials(KeyValuePairs<StakeCredential, i64>),
OtherAccountingPot(Coin),
@ -267,7 +267,7 @@ impl<C> minicbor::encode::Encode<C> for InstantaneousRewardTarget {
}
}
#[derive(Encode, Decode, Debug, PartialEq, PartialOrd)]
#[derive(Encode, Decode, Debug, PartialEq, PartialOrd, Clone)]
#[cbor]
pub struct MoveInstantaneousReward {
#[n(0)]
@ -284,7 +284,7 @@ pub type IPv4 = ByteVec;
pub type IPv6 = ByteVec;
pub type DnsName = String;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum Relay {
SingleHostAddr(Option<Port>, Option<IPv4>, Option<IPv6>),
SingleHostName(Option<Port>, DnsName),
@ -351,7 +351,7 @@ impl<C> minicbor::encode::Encode<C> for Relay {
pub type PoolMetadataHash = Hash<32>;
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct PoolMetadata {
#[n(0)]
pub url: String,
@ -363,7 +363,7 @@ pub struct PoolMetadata {
pub type AddrKeyhash = Hash<28>;
pub type Scripthash = Hash<28>;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub struct RationalNumber {
pub numerator: i64,
pub denominator: u64,
@ -401,7 +401,7 @@ pub type UnitInterval = RationalNumber;
pub type PositiveInterval = RationalNumber;
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)]
pub enum StakeCredential {
AddrKeyhash(AddrKeyhash),
Scripthash(Scripthash),
@ -447,7 +447,7 @@ impl<C> minicbor::encode::Encode<C> for StakeCredential {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum Certificate {
StakeRegistration(StakeCredential),
StakeDeregistration(StakeCredential),
@ -615,7 +615,7 @@ impl<C> minicbor::encode::Encode<C> for Certificate {
}
}
#[derive(Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[cbor(index_only)]
pub enum NetworkId {
#[n(0)]
@ -624,7 +624,7 @@ pub enum NetworkId {
Two,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(index_only)]
pub enum Language {
#[n(0)]
@ -637,7 +637,7 @@ pub type CostMdls = KeyValuePairs<Language, CostModel>;
pub type ProtocolVersion = (u32, u32);
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(map)]
pub struct ProtocolParamUpdate {
#[n(0)]
@ -690,7 +690,7 @@ pub struct ProtocolParamUpdate {
pub max_collateral_inputs: Option<u32>,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct Update {
#[n(0)]
pub proposed_protocol_parameter_updates: KeyValuePairs<Genesishash, ProtocolParamUpdate>,
@ -699,7 +699,7 @@ pub struct Update {
pub epoch: Epoch,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum TransactionBodyComponent {
Inputs(MaybeIndefArray<TransactionInput>),
Outputs(MaybeIndefArray<TransactionOutput>),
@ -814,7 +814,7 @@ impl<C> minicbor::encode::Encode<C> for TransactionBodyComponent {
// Can't derive encode for TransactionBody because it seems to require a very
// particular order for each key in the map
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub struct TransactionBody(Vec<TransactionBodyComponent>);
impl Deref for TransactionBody {
@ -850,7 +850,7 @@ impl<C> minicbor::encode::Encode<C> for TransactionBody {
}
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct VKeyWitness {
#[n(0)]
pub vkey: ByteVec,
@ -859,7 +859,7 @@ pub struct VKeyWitness {
pub signature: ByteVec,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum NativeScript {
ScriptPubkey(AddrKeyhash),
ScriptAll(MaybeIndefArray<NativeScript>),
@ -931,7 +931,7 @@ impl<C> minicbor::encode::Encode<C> for NativeScript {
}
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(transparent)]
pub struct PlutusScript(#[n(0)] pub ByteVec);
@ -947,7 +947,7 @@ big_uint = #6.2(bounded_bytes) ; New
big_nint = #6.3(bounded_bytes) ; New
*/
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum BigInt {
Int(Int),
BigUInt(ByteVec),
@ -1009,7 +1009,7 @@ impl<C> minicbor::encode::Encode<C> for BigInt {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum PlutusData {
Constr(Constr<PlutusData>),
Map(KeyValuePairs<PlutusData, PlutusData>),
@ -1090,7 +1090,7 @@ impl<C> minicbor::encode::Encode<C> for PlutusData {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Constr<A> {
pub tag: u64,
pub any_constructor: Option<u64>,
@ -1159,7 +1159,7 @@ where
}
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct ExUnits {
#[n(0)]
pub mem: u32,
@ -1167,7 +1167,7 @@ pub struct ExUnits {
pub steps: u64,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct ExUnitPrices {
#[n(0)]
mem_price: PositiveInterval,
@ -1176,7 +1176,7 @@ pub struct ExUnitPrices {
step_price: PositiveInterval,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(index_only)]
pub enum RedeemerTag {
#[n(0)]
@ -1189,7 +1189,7 @@ pub enum RedeemerTag {
Reward,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct Redeemer {
#[n(0)]
pub tag: RedeemerTag,
@ -1211,7 +1211,7 @@ pub struct Redeemer {
, attributes : bytes
] */
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct BootstrapWitness {
#[n(0)]
pub public_key: ByteVec,
@ -1226,7 +1226,7 @@ pub struct BootstrapWitness {
pub attributes: ByteVec,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(map)]
pub struct TransactionWitnessSet {
#[n(0)]
@ -1248,7 +1248,7 @@ pub struct TransactionWitnessSet {
pub redeemer: Option<MaybeIndefArray<Redeemer>>,
}
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(map)]
pub struct AlonzoAuxiliaryData {
#[n(0)]
@ -1259,7 +1259,7 @@ pub struct AlonzoAuxiliaryData {
pub plutus_scripts: Option<MaybeIndefArray<PlutusScript>>,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum Metadatum {
Int(Int),
Bytes(ByteVec),
@ -1350,7 +1350,7 @@ pub type MetadatumLabel = AnyUInt;
pub type Metadata = KeyValuePairs<MetadatumLabel, Metadatum>;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum AuxiliaryData {
Shelley(Metadata),
ShelleyMa {
@ -1417,7 +1417,7 @@ impl<C> minicbor::Encode<C> for AuxiliaryData {
pub type TransactionIndex = u32;
#[derive(Encode, Decode, Debug, PartialEq)]
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct Block {
#[n(0)]
pub header: Header,
@ -1459,18 +1459,33 @@ pub struct MintedBlock<'b> {
}
#[derive(Encode, Decode, Debug)]
pub struct Transaction {
pub struct Tx {
#[n(0)]
transaction_body: TransactionBody,
pub transaction_body: TransactionBody,
#[n(1)]
transaction_witness_set: TransactionWitnessSet,
pub transaction_witness_set: TransactionWitnessSet,
#[n(2)]
success: bool,
pub success: bool,
#[n(3)]
auxiliary_data: Option<AuxiliaryData>,
pub auxiliary_data: Option<AuxiliaryData>,
}
#[derive(Encode, Decode, Debug)]
pub struct MintedTx<'b> {
#[b(0)]
pub transaction_body: KeepRaw<'b, TransactionBody>,
#[n(1)]
pub transaction_witness_set: TransactionWitnessSet,
#[n(2)]
pub success: bool,
#[n(3)]
pub auxiliary_data: Option<KeepRaw<'b, AuxiliaryData>>,
}
#[cfg(test)]

View file

@ -13,9 +13,9 @@ impl Address {
#[cfg(test)]
mod tests {
use crate::byron::MintedMainBlock;
use crate::byron::MintedBlock;
type BlockWrapper<'b> = (u16, MintedMainBlock<'b>);
type BlockWrapper<'b> = (u16, MintedBlock<'b>);
const KNOWN_ADDRESSES: &[&str] = &[
"DdzFFzCqrht8QHTQXbWy2qoyPaqTN8BjyfKygGmpy9dtot1tvkBfCaVTnR22XCaaDVn3M1U6aiMShoCLzw6VWSwzQKhhJrM3YjYp3wyy",

View file

@ -48,9 +48,9 @@ impl ToHash<32> for KeepRaw<'_, Tx> {
mod tests {
use pallas_codec::minicbor;
use crate::{byron::MintedMainBlock, ToHash};
use crate::{byron::MintedBlock, ToHash};
type BlockWrapper<'b> = (u16, MintedMainBlock<'b>);
type BlockWrapper<'b> = (u16, MintedBlock<'b>);
const KNOWN_HASH: &'static str =
"5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6";

View file

@ -48,9 +48,9 @@ impl TxPayload {
mod tests {
use pallas_codec::minicbor;
use crate::{byron::MainBlock, ToHash};
use crate::{byron::Block, ToHash};
type BlockWrapper = (u16, MainBlock);
type BlockWrapper = (u16, Block);
#[test]
fn known_fee_matches() {

View file

@ -51,7 +51,7 @@ pub type Attributes = EmptyMap;
// Addresses
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum AddrDistr {
Variant0(StakeholderId),
Variant1,
@ -96,7 +96,7 @@ impl minicbor::Encode<()> for AddrDistr {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum AddrType {
PubKey,
Script,
@ -137,7 +137,7 @@ impl<C> minicbor::Encode<C> for AddrType {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum AddrAttrProperty {
AddrDistr(AddrDistr),
Bytes(ByteVec),
@ -187,7 +187,7 @@ impl<C> minicbor::Encode<C> for AddrAttrProperty {
pub type AddrAttr = OrderPreservingProperties<AddrAttrProperty>;
#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, Clone)]
pub struct AddressPayload {
#[n(0)]
pub root: AddressId,
@ -200,7 +200,7 @@ pub struct AddressPayload {
}
// address = [ #6.24(bytes .cbor ([addressid, addrattr, addrtype])), u64 ]
#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, Clone)]
pub struct Address {
#[n(0)]
pub payload: CborWrap<AddressPayload>,
@ -212,7 +212,7 @@ pub struct Address {
// Transactions
// txout = [address, u64]
#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, Clone)]
pub struct TxOut {
#[n(0)]
pub address: Address,
@ -221,7 +221,7 @@ pub struct TxOut {
pub amount: u64,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum TxIn {
// [0, #6.24(bytes .cbor ([txid, u32]))]
Variant0(CborWrap<(TxId, u32)>),
@ -269,7 +269,7 @@ impl<C> minicbor::Encode<C> for TxIn {
}
// tx = [[+ txin], [+ txout], attributes]
#[derive(Debug, Encode, Decode)]
#[derive(Debug, Encode, Decode, Clone)]
pub struct Tx {
#[n(0)]
pub inputs: MaybeIndefArray<TxIn>,
@ -287,7 +287,7 @@ pub type TxProof = (u32, ByronHash, ByronHash);
pub type ValidatorScript = (u16, ByteVec);
pub type RedeemerScript = (u16, ByteVec);
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Twit {
// [0, #6.24(bytes .cbor ([pubkey, signature]))]
PkWitness(CborWrap<(PubKey, Signature)>),
@ -841,7 +841,6 @@ pub struct BlockHead {
pub extra_data: BlockHeadEx,
}
// [tx, [* twit]]
#[derive(Debug, Encode, Decode)]
pub struct TxPayload {
#[n(0)]
@ -851,6 +850,15 @@ pub struct TxPayload {
pub witness: MaybeIndefArray<Twit>,
}
#[derive(Debug, Encode, Decode, Clone)]
pub struct MintedTxPayload<'b> {
#[b(0)]
pub transaction: KeepRaw<'b, Tx>,
#[n(1)]
pub witness: MaybeIndefArray<Twit>,
}
#[derive(Encode, Decode, Debug)]
pub struct BlockBody {
#[n(0)]
@ -866,6 +874,21 @@ pub struct BlockBody {
pub upd_payload: Up,
}
#[derive(Encode, Decode, Debug)]
pub struct MintedBlockBody<'b> {
#[b(0)]
pub tx_payload: MaybeIndefArray<MintedTxPayload<'b>>,
#[b(1)]
pub ssc_payload: Ssc,
#[b(2)]
pub dlg_payload: MaybeIndefArray<Dlg>,
#[b(3)]
pub upd_payload: Up,
}
// Epoch Boundary Blocks
#[derive(Encode, Decode, Debug)]
@ -896,7 +919,7 @@ pub struct EbbHead {
}
#[derive(Encode, Decode, Debug)]
pub struct MainBlock {
pub struct Block {
#[n(0)]
pub header: BlockHead,
@ -908,12 +931,12 @@ pub struct MainBlock {
}
#[derive(Encode, Decode, Debug)]
pub struct MintedMainBlock<'b> {
pub struct MintedBlock<'b> {
#[b(0)]
pub header: KeepRaw<'b, BlockHead>,
#[n(1)]
pub body: BlockBody,
#[b(1)]
pub body: MintedBlockBody<'b>,
#[n(2)]
pub extra: MaybeIndefArray<Attributes>,
@ -933,7 +956,7 @@ pub struct EbBlock {
#[cfg(test)]
mod tests {
use super::{BlockHead, EbBlock, MintedMainBlock};
use super::{BlockHead, EbBlock, MintedBlock};
use pallas_codec::minicbor::{self, to_vec};
#[test]
@ -958,7 +981,7 @@ mod tests {
#[test]
fn main_block_isomorphic_decoding_encoding() {
type BlockWrapper<'b> = (u16, MintedMainBlock<'b>);
type BlockWrapper<'b> = (u16, MintedBlock<'b>);
let test_blocks = vec![
//include_str!("../../../test_data/genesis.block"),

View file

@ -24,9 +24,9 @@ impl EbbHead {
mod tests {
use pallas_codec::minicbor;
use crate::byron::MainBlock;
use crate::byron::Block;
type BlockWrapper = (u16, MainBlock);
type BlockWrapper = (u16, Block);
#[test]
fn knwon_slot_matches() {

View file

@ -23,15 +23,6 @@ where
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Era {
Byron,
Shelley,
Allegra, // time-locks
Mary, // multi-assets
Alonzo, // smart-contracts
}
#[cfg(feature = "json")]
pub trait ToCanonicalJson {
fn to_json(&self) -> serde_json::Value;

View file

@ -4,6 +4,5 @@ mod framework;
pub mod alonzo;
pub mod byron;
pub mod probing;
pub use framework::*;

View file

@ -0,0 +1,20 @@
[package]
name = "pallas-traverse"
description = "Utilities to traverse over multi-era block data"
version = "0.11.0-alpha.0"
edition = "2021"
repository = "https://github.com/txpipe/pallas"
homepage = "https://github.com/txpipe/pallas"
documentation = "https://docs.rs/pallas-traverse"
license = "Apache-2.0"
readme = "README.md"
authors = [
"Santiago Carmuega <santiago@carmuega.me>",
]
[dependencies]
pallas-primitives = { version = "0.11.0-alpha.0", path = "../pallas-primitives" }
pallas-crypto = { version = "0.11.0-alpha.0", path = "../pallas-crypto" }
pallas-codec = { version = "0.11.0-alpha.0", path = "../pallas-codec" }
hex = "0.4.3"
thiserror = "1.0.31"

View file

@ -0,0 +1,63 @@
use pallas_codec::minicbor;
use pallas_crypto::hash::Hash;
use pallas_primitives::{alonzo, byron, ToHash};
use crate::{probe, Era, Error, MultiEraBlock};
type BlockWrapper<T> = (u16, T);
impl<'b> MultiEraBlock<'b> {
pub fn from_epoch_boundary(block: byron::EbBlock) -> Self {
Self::EpochBoundary(Box::new(block))
}
pub fn from_byron(block: byron::MintedBlock<'b>) -> Self {
Self::Byron(Box::new(block))
}
pub fn from_alonzo_compatible(block: alonzo::MintedBlock<'b>) -> Self {
Self::AlonzoCompatible(Box::new(block))
}
pub fn decode(cbor: &'b [u8]) -> Result<MultiEraBlock<'b>, Error> {
match probe::block_era(cbor) {
probe::Outcome::EpochBoundary => {
let (_, block): BlockWrapper<byron::EbBlock> =
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(MultiEraBlock::from_epoch_boundary(block))
}
probe::Outcome::Matched(era) => match era {
Era::Byron => {
let (_, block): BlockWrapper<byron::MintedBlock> =
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(Self::from_byron(block))
}
Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => {
let (_, block): BlockWrapper<alonzo::MintedBlock> =
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(Self::from_alonzo_compatible(block))
}
},
probe::Outcome::Inconclusive => Err(Error::unknown_cbor(cbor)),
}
}
pub fn hash(&self) -> Hash<32> {
match self {
MultiEraBlock::EpochBoundary(x) => x.header.to_hash(),
MultiEraBlock::AlonzoCompatible(x) => x.header.to_hash(),
MultiEraBlock::Byron(x) => x.header.to_hash(),
}
}
pub fn slot(&self) -> u64 {
match self {
MultiEraBlock::EpochBoundary(x) => x.header.to_abs_slot(),
MultiEraBlock::AlonzoCompatible(x) => x.header.header_body.slot,
MultiEraBlock::Byron(x) => x.header.consensus_data.0.to_abs_slot(),
}
}
}

View file

@ -0,0 +1,96 @@
//! Iterate over block data
use pallas_primitives::{alonzo, byron};
use crate::{MultiEraBlock, MultiEraTx};
fn clone_alonzo_tx_at<'b>(
block: &'b alonzo::MintedBlock,
index: usize,
) -> Option<alonzo::MintedTx<'b>> {
let transaction_body = block.transaction_bodies.get(index).cloned()?;
let transaction_witness_set = block.transaction_witness_sets.get(index).cloned()?;
let success = block
.invalid_transactions
.as_ref()?
.contains(&(index as u32));
let auxiliary_data = block
.auxiliary_data_set
.iter()
.find_map(|(idx, val)| {
if idx.eq(&(index as u32)) {
Some(val)
} else {
None
}
})
.cloned();
Some(alonzo::MintedTx {
transaction_body,
transaction_witness_set,
success,
auxiliary_data,
})
}
fn clone_byron_tx_at<'b>(
block: &'b byron::MintedBlock,
index: usize,
) -> Option<byron::MintedTxPayload<'b>> {
block.body.tx_payload.get(index).cloned()
}
pub struct TxIter<'b> {
block: &'b MultiEraBlock<'b>,
index: usize,
}
impl<'b> Iterator for TxIter<'b> {
type Item = MultiEraTx<'b>;
fn next(&mut self) -> Option<Self::Item> {
let tx = match self.block {
MultiEraBlock::EpochBoundary(_) => None,
MultiEraBlock::AlonzoCompatible(x) => {
clone_alonzo_tx_at(x, self.index).map(MultiEraTx::from_alonzo_compatible)
}
MultiEraBlock::Byron(x) => clone_byron_tx_at(x, self.index).map(MultiEraTx::from_byron),
}?;
self.index += 1;
Some(tx)
}
}
impl<'b> MultiEraBlock<'b> {
pub fn tx_iter(&'b self) -> TxIter<'b> {
TxIter {
index: 0,
block: self,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iteration() {
let blocks = vec![
(include_str!("../../test_data/byron2.block"), 2usize),
(include_str!("../../test_data/shelley1.block"), 0),
(include_str!("../../test_data/mary1.block"), 0),
(include_str!("../../test_data/allegra1.block"), 0),
(include_str!("../../test_data/alonzo1.block"), 5),
];
for (block_str, tx_count) in blocks.into_iter() {
let cbor = hex::decode(block_str).expect("invalid hex");
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
assert_eq!(block.tx_iter().count(), tx_count);
}
}
}

View file

@ -0,0 +1,54 @@
//! Utilities to traverse over multi-era block data
use std::fmt::Display;
use pallas_primitives::{alonzo, byron};
use thiserror::Error;
pub mod block;
pub mod iter;
pub mod probe;
pub mod tx;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Era {
Byron,
Shelley,
Allegra, // time-locks
Mary, // multi-assets
Alonzo, // smart-contracts
}
#[derive(Debug)]
#[non_exhaustive]
pub enum MultiEraTx<'b> {
AlonzoCompatible(Box<alonzo::MintedTx<'b>>),
Byron(Box<byron::MintedTxPayload<'b>>),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum MultiEraBlock<'b> {
EpochBoundary(Box<byron::EbBlock>),
AlonzoCompatible(Box<alonzo::MintedBlock<'b>>),
Byron(Box<byron::MintedBlock<'b>>),
}
#[derive(Debug, Error)]
pub enum Error {
#[error("Invalid CBOR structure: {0}")]
InvalidCbor(String),
#[error("Unknown CBOR structure: {0}")]
UnknownCbor(String),
}
impl Error {
pub fn invalid_cbor(error: impl Display) -> Self {
Error::InvalidCbor(format!("{}", error))
}
pub fn unknown_cbor(bytes: &[u8]) -> Self {
Error::UnknownCbor(hex::encode(bytes))
}
}

View file

@ -1,4 +1,4 @@
//! Heuristics for detecting cbor content without decoding
//! Lightweight inspection of block data without full CBOR decoding
use pallas_codec::minicbor::decode::{Token, Tokenizer};
@ -12,9 +12,9 @@ pub enum Outcome {
}
// Executes a very lightweight inspection of the initial tokens of the CBOR
// payload and infers with a certain degree of confidence the type of Cardano
// structure within.
pub fn probe_block_cbor_era(cbor: &[u8]) -> Outcome {
// block payload to extract the tag of the block wrapper which defines the era
// of the contained bytes.
pub fn block_era(cbor: &[u8]) -> Outcome {
let mut tokenizer = Tokenizer::new(cbor);
if !matches!(tokenizer.next(), Some(Ok(Token::Array(2)))) {
@ -44,7 +44,7 @@ mod tests {
let block_str = include_str!("../../test_data/genesis.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::EpochBoundary));
}
@ -54,7 +54,7 @@ mod tests {
let block_str = include_str!("../../test_data/byron1.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::Matched(Era::Byron)));
}
@ -64,7 +64,7 @@ mod tests {
let block_str = include_str!("../../test_data/shelley1.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::Matched(Era::Shelley)));
}
@ -74,7 +74,7 @@ mod tests {
let block_str = include_str!("../../test_data/allegra1.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::Matched(Era::Allegra)));
}
@ -84,7 +84,7 @@ mod tests {
let block_str = include_str!("../../test_data/mary1.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::Matched(Era::Mary)));
}
@ -94,7 +94,7 @@ mod tests {
let block_str = include_str!("../../test_data/alonzo1.block");
let bytes = hex::decode(block_str).unwrap();
let inference = probe_block_cbor_era(bytes.as_slice());
let inference = block_era(bytes.as_slice());
assert!(matches!(inference, Outcome::Matched(Era::Alonzo)));
}

13
pallas-traverse/src/tx.rs Normal file
View file

@ -0,0 +1,13 @@
use pallas_primitives::{alonzo, byron};
use crate::MultiEraTx;
impl<'b> MultiEraTx<'b> {
pub fn from_byron(tx: byron::MintedTxPayload<'b>) -> Self {
Self::Byron(Box::new(tx))
}
pub fn from_alonzo_compatible(tx: alonzo::MintedTx<'b>) -> Self {
Self::AlonzoCompatible(Box::new(tx))
}
}

View file

@ -16,5 +16,6 @@ authors = [
pallas-multiplexer = { version = "0.11.0-alpha.0", path = "../pallas-multiplexer/" }
pallas-miniprotocols = { version = "0.11.0-alpha.0", path = "../pallas-miniprotocols/" }
pallas-primitives = { version = "0.11.0-alpha.0", path = "../pallas-primitives/" }
pallas-traverse = { version = "0.11.0-alpha.0", path = "../pallas-traverse/" }
pallas-crypto = { version = "0.11.0-alpha.0", path = "../pallas-crypto/" }
pallas-codec = { version = "0.11.0-alpha.0", path = "../pallas-codec/" }

View file

@ -2,3 +2,6 @@
#[doc(inline)]
pub use pallas_primitives as primitives;
#[doc(inline)]
pub use pallas_traverse as traverse;