feat: Introduce 'traverse' library (#117)

This commit is contained in:
Santiago Carmuega 2022-06-12 19:09:33 -03:00 committed by GitHub
parent c9489c3ddf
commit 6913efef78
20 changed files with 377 additions and 106 deletions

View file

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

View file

@ -42,6 +42,7 @@ As already explained, _Pallas_ aims at being an expanding set of components. The
| Crates | Description | | Crates | Description |
| --------------------------------------- | ----------------------------------------------------------------------- | | --------------------------------------- | ----------------------------------------------------------------------- |
| [pallas-primitives](/pallas-primitives) | Ledger primitives and cbor codec for the different Cardano eras | | [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-ticking | Time passage implementation for consensus algorithm |
| pallas-applying | Logic for validating and applying new blocks and txs to the chain state | | 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 | | 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() { fn main() {
let blocks = vec![ let blocks = vec![
@ -10,23 +10,14 @@ fn main() {
]; ];
for block_str in blocks.iter() { 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) { let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
probing::Outcome::Matched(era) => match era {
Era::Byron => { println!("{} {}", block.slot(), block.hash());
let (_, block): (u16, byron::MainBlock) =
pallas::codec::minicbor::decode(&bytes).expect("invalid cbor"); for tx in block.tx_iter() {
println!("{:?}", block) 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 /// 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> { pub enum MaybeIndefArray<A> {
Def(Vec<A>), Def(Vec<A>),
Indef(Vec<A>), Indef(Vec<A>),
@ -186,7 +186,7 @@ where
/// transform key-value structures into an orderer vec of `properties`, 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 /// each entry represents a a cbor-encodable variant of an attribute of the
/// struct. /// struct.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct OrderPreservingProperties<P>(Vec<P>); pub struct OrderPreservingProperties<P>(Vec<P>);
impl<P> Deref for OrderPreservingProperties<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 /// Wraps a struct so that it is encoded/decoded as a cbor bytes
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct CborWrap<T>(pub T); pub struct CborWrap<T>(pub T);
impl<'b, C, T> minicbor::Decode<'b, C> for CborWrap<T> impl<'b, C, T> minicbor::Decode<'b, C> for CborWrap<T>
@ -312,7 +312,7 @@ where
/// An empty map /// An empty map
/// ///
/// don't ask me why, that's what the CDDL asks for. /// don't ask me why, that's what the CDDL asks for.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct EmptyMap; pub struct EmptyMap;
impl<'b, C> minicbor::decode::Decode<'b, C> for 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(); /// let confirm: (u16, u16) = minicbor::decode(keeper.raw_cbor()).unwrap();
/// assert_eq!(confirm, (456u16, 789u16)); /// assert_eq!(confirm, (456u16, 789u16));
/// ``` /// ```
#[derive(Debug, PartialEq, PartialOrd)] #[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct KeepRaw<'b, T> { pub struct KeepRaw<'b, T> {
raw: &'b [u8], raw: &'b [u8],
inner: T, inner: T,

View file

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

View file

@ -13,9 +13,9 @@ impl Address {
#[cfg(test)] #[cfg(test)]
mod tests { 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] = &[ const KNOWN_ADDRESSES: &[&str] = &[
"DdzFFzCqrht8QHTQXbWy2qoyPaqTN8BjyfKygGmpy9dtot1tvkBfCaVTnR22XCaaDVn3M1U6aiMShoCLzw6VWSwzQKhhJrM3YjYp3wyy", "DdzFFzCqrht8QHTQXbWy2qoyPaqTN8BjyfKygGmpy9dtot1tvkBfCaVTnR22XCaaDVn3M1U6aiMShoCLzw6VWSwzQKhhJrM3YjYp3wyy",

View file

@ -48,9 +48,9 @@ impl ToHash<32> for KeepRaw<'_, Tx> {
mod tests { mod tests {
use pallas_codec::minicbor; 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 = const KNOWN_HASH: &'static str =
"5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6"; "5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6";

View file

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

View file

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

View file

@ -24,9 +24,9 @@ impl EbbHead {
mod tests { mod tests {
use pallas_codec::minicbor; use pallas_codec::minicbor;
use crate::byron::MainBlock; use crate::byron::Block;
type BlockWrapper = (u16, MainBlock); type BlockWrapper = (u16, Block);
#[test] #[test]
fn knwon_slot_matches() { 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")] #[cfg(feature = "json")]
pub trait ToCanonicalJson { pub trait ToCanonicalJson {
fn to_json(&self) -> serde_json::Value; fn to_json(&self) -> serde_json::Value;

View file

@ -4,6 +4,5 @@ mod framework;
pub mod alonzo; pub mod alonzo;
pub mod byron; pub mod byron;
pub mod probing;
pub use framework::*; 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}; 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 // 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 // block payload to extract the tag of the block wrapper which defines the era
// structure within. // of the contained bytes.
pub fn probe_block_cbor_era(cbor: &[u8]) -> Outcome { pub fn block_era(cbor: &[u8]) -> Outcome {
let mut tokenizer = Tokenizer::new(cbor); let mut tokenizer = Tokenizer::new(cbor);
if !matches!(tokenizer.next(), Some(Ok(Token::Array(2)))) { 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 block_str = include_str!("../../test_data/genesis.block");
let bytes = hex::decode(block_str).unwrap(); 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)); assert!(matches!(inference, Outcome::EpochBoundary));
} }
@ -54,7 +54,7 @@ mod tests {
let block_str = include_str!("../../test_data/byron1.block"); let block_str = include_str!("../../test_data/byron1.block");
let bytes = hex::decode(block_str).unwrap(); 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))); assert!(matches!(inference, Outcome::Matched(Era::Byron)));
} }
@ -64,7 +64,7 @@ mod tests {
let block_str = include_str!("../../test_data/shelley1.block"); let block_str = include_str!("../../test_data/shelley1.block");
let bytes = hex::decode(block_str).unwrap(); 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))); assert!(matches!(inference, Outcome::Matched(Era::Shelley)));
} }
@ -74,7 +74,7 @@ mod tests {
let block_str = include_str!("../../test_data/allegra1.block"); let block_str = include_str!("../../test_data/allegra1.block");
let bytes = hex::decode(block_str).unwrap(); 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))); assert!(matches!(inference, Outcome::Matched(Era::Allegra)));
} }
@ -84,7 +84,7 @@ mod tests {
let block_str = include_str!("../../test_data/mary1.block"); let block_str = include_str!("../../test_data/mary1.block");
let bytes = hex::decode(block_str).unwrap(); 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))); assert!(matches!(inference, Outcome::Matched(Era::Mary)));
} }
@ -94,7 +94,7 @@ mod tests {
let block_str = include_str!("../../test_data/alonzo1.block"); let block_str = include_str!("../../test_data/alonzo1.block");
let bytes = hex::decode(block_str).unwrap(); 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))); 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-multiplexer = { version = "0.11.0-alpha.0", path = "../pallas-multiplexer/" }
pallas-miniprotocols = { version = "0.11.0-alpha.0", path = "../pallas-miniprotocols/" } pallas-miniprotocols = { version = "0.11.0-alpha.0", path = "../pallas-miniprotocols/" }
pallas-primitives = { version = "0.11.0-alpha.0", path = "../pallas-primitives/" } 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-crypto = { version = "0.11.0-alpha.0", path = "../pallas-crypto/" }
pallas-codec = { version = "0.11.0-alpha.0", path = "../pallas-codec/" } pallas-codec = { version = "0.11.0-alpha.0", path = "../pallas-codec/" }

View file

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