diff --git a/Cargo.toml b/Cargo.toml index e6ccc6a..9b35d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ "pallas-blockfetch", "pallas-chainsync", "pallas-txsubmission", + "pallas-alonzo", "pallas", ] \ No newline at end of file diff --git a/pallas-alonzo/Cargo.toml b/pallas-alonzo/Cargo.toml new file mode 100644 index 0000000..23bc07e --- /dev/null +++ b/pallas-alonzo/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "pallas-alonzo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +minicbor = "0.11.5" +minicbor-derive = "0.7.2" +hex = "0.4.3" +minicbor-io = "0.6.0" +log = "0.4.14" diff --git a/pallas-alonzo/src/lib.rs b/pallas-alonzo/src/lib.rs new file mode 100644 index 0000000..01f16af --- /dev/null +++ b/pallas-alonzo/src/lib.rs @@ -0,0 +1,539 @@ +use log::warn; +use minicbor::{bytes::ByteVec, Encode}; +use minicbor_derive::{Decode, Encode}; +use std::collections::{BTreeMap, HashMap}; + +#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)] +pub struct SkipCbor {} + +impl<'b, const N: usize> minicbor::Decode<'b> for SkipCbor { + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + { + let probe = d.probe(); + println!("skipped cbor value {}: {:?}", N, probe.datatype()?); + } + + d.skip()?; + Ok(SkipCbor {}) + } +} + +impl minicbor::Encode for SkipCbor { + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + +pub type SomeSkipCbor = SkipCbor<0>; + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct VrfCert(#[n(0)] ByteVec, #[n(1)] ByteVec); + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct HeaderBody { + #[n(0)] + block_number: u64, + + #[n(1)] + slot: u64, + + #[n(2)] + prev_hash: ByteVec, + + #[n(3)] + issuer_vkey: ByteVec, + + #[n(4)] + vrf_vkey: ByteVec, + + #[n(5)] + nonce_vrf: VrfCert, + + #[n(6)] + leader_vrf: VrfCert, + + #[n(7)] + block_body_size: u64, + + #[n(8)] + block_body_hash: ByteVec, + + #[n(9)] + operational_cert: ByteVec, + + #[n(10)] + unknown_0: u64, + + #[n(11)] + unknown_1: u64, + + #[n(12)] + unknown_2: ByteVec, + + #[n(13)] + protocol_version_major: u64, + + #[n(14)] + protocol_version_minor: u64, +} + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct KesSignature {} + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct Header { + #[n(0)] + header_body: HeaderBody, + + #[n(1)] + body_signature: ByteVec, +} + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct TransactionInput { + #[n(0)] + pub transaction_id: ByteVec, + + #[n(1)] + pub index: u64, +} + +pub type ScriptHash = ByteVec; + +pub type PolicyId = ScriptHash; + +pub type AssetName = ByteVec; + +pub type Multiasset = HashMap>; + +pub type Mint = Multiasset; + +pub type Coin = u64; + +#[derive(Debug, PartialEq)] +pub enum Value { + Coin(Coin), + Multiasset(Coin, Multiasset), +} + +impl<'b> minicbor::decode::Decode<'b> for Value { + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + match d.datatype()? { + minicbor::data::Type::U32 => Ok(Value::Coin(d.decode()?)), + minicbor::data::Type::U64 => Ok(Value::Coin(d.decode()?)), + minicbor::data::Type::Array => { + d.array()?; + let coin = d.u64()?; + let multiasset = d.decode()?; + Ok(Value::Multiasset(coin, multiasset)) + } + _ => Err(minicbor::decode::Error::Message( + "unknown cbor data type for Alonzo Value enum", + )), + } + } +} + +impl minicbor::encode::Encode for Value { + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct TransactionOutput { + #[n(0)] + pub address: ByteVec, + + #[n(1)] + pub amount: Value, + + #[n(2)] + pub datum_hash: Option, +} + +pub type StakeCredential = SkipCbor<1>; +pub type PoolKeyhash = SkipCbor<2>; +pub type PoolParams = SkipCbor<3>; +pub type Epoch = SkipCbor<4>; +pub type Genesishash = SkipCbor<5>; +pub type GenesisDelegateHash = SkipCbor<6>; +pub type VrfKeyhash = SkipCbor<7>; +pub type MoveInstantaneousReward = SkipCbor<8>; +pub type Margin = SkipCbor<9>; +pub type RewardAccount = SkipCbor<10>; +pub type PoolOwners = SkipCbor<11>; +pub type Relay = SkipCbor<12>; +pub type PoolMetadata = SkipCbor<13>; +pub type AddrKeyhash = SkipCbor<14>; +pub type UnitInterval = SkipCbor<15>; + +#[derive(Debug, PartialEq)] +pub enum Certificate { + StakeRegistration(StakeCredential), + StakeDeregistration(StakeCredential), + StakeDelegation(StakeCredential, PoolKeyhash), + PoolRegistration { + operator: PoolKeyhash, + vrf_keyhash: VrfKeyhash, + pledge: Coin, + cost: Coin, + margin: UnitInterval, + reward_account: RewardAccount, + pool_owners: Vec, + relays: Vec, + pool_metadata: Option, + }, + PoolRetirement(PoolKeyhash, Epoch), + GenesisKeyDelegation(Genesishash, GenesisDelegateHash, VrfKeyhash), + MoveInstantaneousRewardsCert(MoveInstantaneousReward), +} + +impl<'b> minicbor::decode::Decode<'b> for Certificate { + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + println!("{:?}", d.array()?); + + let variant = d.u16()?; + + match variant { + 0 => { + let a = d.decode()?; + Ok(Certificate::StakeRegistration(a)) + } + 1 => { + let a = d.decode()?; + Ok(Certificate::StakeDeregistration(a)) + } + 2 => { + let a = d.decode()?; + let b = d.decode()?; + Ok(Certificate::StakeDelegation(a, b)) + } + 3 => { + let operator = d.decode()?; + let vrf_keyhash = d.decode()?; + let pledge = d.decode()?; + let cost = d.decode()?; + let margin = d.decode()?; + let reward_account = d.decode()?; + let pool_owners = d.decode()?; + let relays = d.decode()?; + let pool_metadata = d.decode()?; + + Ok(Certificate::PoolRegistration { + operator, + vrf_keyhash, + pledge, + cost, + margin, + reward_account, + pool_owners, + relays, + pool_metadata, + }) + } + 4 => { + let a = d.decode()?; + let b = d.decode()?; + Ok(Certificate::PoolRetirement(a, b)) + } + 5 => { + let a = d.decode()?; + let b = d.decode()?; + let c = d.decode()?; + Ok(Certificate::GenesisKeyDelegation(a, b, c)) + } + 6 => { + let a = d.decode()?; + Ok(Certificate::MoveInstantaneousRewardsCert(a)) + } + _ => Err(minicbor::decode::Error::Message( + "unknown variant id for certificate", + )), + } + } +} + +impl minicbor::encode::Encode for Certificate { + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + +#[derive(Encode, Decode, Debug, PartialEq)] +#[cbor(map)] +pub struct TransactionBody { + #[n(0)] + pub inputs: Vec, + + #[n(1)] + pub outputs: Vec, + + #[n(2)] + pub fee: u64, + + #[n(3)] + pub ttl: Option, + + #[n(4)] + pub certificates: Option>, + + #[n(5)] + pub withdrawals: Option, + + #[n(6)] + pub update: Option, + + #[n(7)] + pub auxiliary_data_hash: Option, + + #[n(8)] + pub validity_interval_start: Option, + + #[n(9)] + pub mint: Option, + + #[n(11)] + pub script_data_hash: Option, + + #[n(13)] + pub collateral: Option>, + + #[n(14)] + pub required_signers: Option, + + #[n(15)] + pub network_id: Option, +} + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct VKeyWitness { + #[n(0)] + pub vkey: SomeSkipCbor, + + #[n(1)] + pub signature: SomeSkipCbor, +} + +#[derive(Encode, Decode, Debug, PartialEq)] +#[cbor(map)] +pub struct TransactionWitnessSets { + #[n(0)] + pub vkeywitness: Vec, // Option, + + #[n(1)] + pub multisig_script: Option, + + #[n(2)] + pub bootstrap_witness: Option, + + #[n(3)] + pub monetary_policy_script: Option, + + #[n(4)] + pub plutus_script: Option, +} + +#[derive(Encode, Decode, Debug, PartialEq)] +#[cbor(map)] +pub struct AlonzoAuxiliaryData { + #[n(0)] + pub metadata: Option, + #[n(1)] + pub native_script: Option>, + #[n(2)] + pub plutus_script: Option>, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Metadatum { + Int(i64), + Bytes(ByteVec), + Text(String), + Array(Vec), + Map(BTreeMap), +} + +impl<'b> minicbor::Decode<'b> for Metadatum { + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + match d.datatype()? { + minicbor::data::Type::U8 => { + let i = d.u8()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::U16 => { + let i = d.u16()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::U32 => { + let i = d.u32()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::U64 => { + let i = d.u64()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::I8 => { + let i = d.i8()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::I16 => { + let i = d.i16()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::I32 => { + let i = d.i32()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::I64 => { + let i = d.i64()?; + Ok(Metadatum::Int(i as i64)) + } + minicbor::data::Type::Bytes => Ok(Metadatum::Bytes(d.decode()?)), + minicbor::data::Type::String => Ok(Metadatum::Text(d.decode()?)), + minicbor::data::Type::Array => Ok(Metadatum::Array(d.decode()?)), + minicbor::data::Type::Map => Ok(Metadatum::Map(d.decode()?)), + _ => Err(minicbor::decode::Error::Message( + "Can't turn data type into metadatum", + )), + } + } +} + +impl minicbor::Encode for Metadatum { + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + +pub type Metadata = BTreeMap; + +#[derive(Debug, PartialEq)] +pub enum AuxiliaryData { + Shelley(Metadata), + ShelleyMa { + transaction_metadata: Metadata, + auxiliary_scripts: Vec, + }, + Alonzo(AlonzoAuxiliaryData), +} + +impl<'b> minicbor::Decode<'b> for AuxiliaryData { + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + match d.datatype()? { + minicbor::data::Type::Map => Ok(AuxiliaryData::Shelley(d.decode()?)), + minicbor::data::Type::Array => { + d.array()?; + let transaction_metadata = d.decode()?; + let auxiliary_scripts = d.decode()?; + Ok(AuxiliaryData::ShelleyMa { + transaction_metadata, + auxiliary_scripts, + }) + } + minicbor::data::Type::Tag => { + d.tag()?; + Ok(AuxiliaryData::Alonzo(d.decode()?)) + } + _ => Err(minicbor::decode::Error::Message( + "Can't infer variant from data type for AuxiliaryData", + )), + } + } +} + +impl minicbor::Encode for AuxiliaryData { + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + +pub type TransactionIndex = u32; + +#[derive(Encode, Decode, Debug, PartialEq)] +pub struct Block { + #[n(0)] + pub header: Header, + + #[n(1)] + pub transaction_bodies: Vec, + + #[n(2)] + pub transaction_witness_sets: Vec, + + #[n(3)] + pub auxiliary_data_set: HashMap, + + #[n(4)] + pub invalid_transactions: Vec, +} + +impl TryFrom<&[u8]> for Block { + type Error = minicbor::decode::Error; + + fn try_from(value: &[u8]) -> Result { + // Hack to unwrap root array on top of the block. Can't find spec explaining + // what this value means. + let (_unknown, block): (u16, Block) = minicbor::decode(value)?; + Ok(block) + } +} + +impl TryFrom<&[u8]> for Header { + type Error = minicbor::decode::Error; + + fn try_from(value: &[u8]) -> Result { + minicbor::decode(value) + } +} + +#[cfg(test)] +mod tests { + use crate::{Block, Header}; + + //const TEST_BODY: &str = + // "85828F1A002F48731A0295ECCF58205A6D350C1FAD5B631779380C3C46D79FB11B57FFC85283284703D23A80DFB6FC58207A41F85C0098FE985D353B2E5F7857817FA2B9381E69D82F310F8CC234CBECC258201406E0E6002D049FA58A8120F3DD925E10B99DEA4BCCABC4082FA91BED2E8DCE8258402BC7FD7CE46DC0E0EBAABA32C6B44EDA4456A2824BF7D0708686F8CBCEC15F548464FE38AA497D292DDD490FB3DBFB34ED850C959DE7378E121FE0159AF033775850717C4B0C95096288054F1492B8FD99A9C4580FB7FB769575D7EC349D1E041C2496680B83F5510EAFB91ED8ED51A7647666EA4377DBF5326885F35933C96F377FC787C566D8CA2A266BB60BCA3A9AD70A825840000454DF44A1A3953A0118DBE89C9B0B4E6A6CC5E1AFB502593C6E1CD769DBBC8BFDEA890115F94F2F31A6DFE7180DE8CBB6F9AF6199B570A1A5E4875721A379585064E02BB0CF4A19EF7D4A6D6C2B6393B33AD8F94F00C4A01271A33BC9AD816E27FC2E1EA521C699AB8B8E57FA4908B299B3FBBB9655030D4A86874577F5F9EB19D6D8E8520B0E86127E5EA458F43BA40319033B582076B1655BC1D9BB338034AEC6764EA55F3B3A463625C1A2CC1684F8685B58E4B858205FF5BE25CA3FC8040B9A2D2D1AEB323BE1F89CDF913C0AB23451199098E20E200519014458408F55CF769916B900A08201C01300A2ED84CBDCD4875EBD3968B72AC47D6FDD9D341277F0F13C33FBE476FA8E8E5FCD7C4C7FAFC69B2A4E9AA4B4E2179B62470E06005901C061EF4EC195563108A8FE8A47E93C4F250F61702073ADB7535AB7135058333F1512F004E68C1233B6B0168703E964AFB177A82D8BA1C4A96083A8F7A12BDB0505990D95B3F8B197E5DA7AB84421BDE583B0E9E9753BE70BA570114DB9840A33E3E4C9C6A763C80EBFC97D34D6E934BC26BE37C743F4EF7B94D010F319BB0E74BA278DBBE633A49F90294075A0F27F1E34209A207EBB96477730B7ACC6A0D05F60F21A7678B9638391F226F91B2E79B615051796A62ED910C002AFC50A706AB68AC2A854A2A75300F5880F7D0E6B4FBAC9E9CFE989CB57E77EA4DA5B7BA1415429C97A16DAF33074F87EC96099C35FC2A01B4FF57B948FCB5D50CCCCDBE0662240796D7EC340FA2DDA273252F590BC44E8D11CBE34DAE4F0F8F76FCCE800FF9C8733E95DFB145A2F56E508E2EA567B8D1681797F3D31D5CB0B93987E5FCC4AB7C40CFEC82F9B2ABAA2DFE22ABFF7ADF513A3DFE0867BE55CC1DE3292AF7D34F22D2686593E6537B9DD00337A61601E163ED66736EC3C75AA13D757DC64E4DD14F069C7564EA061584F2B5EF96B28C1EC218C288845D17E05E14D5CEBF7D7425526A98BA8D05504B3F68A3F8066B8EACB199E193014E842D79F260D5F2673E74AF382A40081825820D9F9EB10FA009216EF3928A457B2BA00E184850A25F6C99F89EBB144931B47220001818258390076B0D16F5D09AC02DD1786981066F6FEDF7AC165A08B3B6F0FB33F039BF76872CE2FC9DEBC7C431CE4700EA060A5AEFDEC0A173D8ED6B4261A032D4D4D021A00029D59075820104A2697C9A493781B519E5D8261EFE9B94D921EC5FC4F1890800A957CA21B55A40081825820EF52C7F6EA9E0CB1A0ED270BBBEB1E07808A386DB77FD738B1F1076AB14E66C4000181825839009493315CD92EB5D8C4304E67B7E16AE36D61D34502694657811A2C8E32C728D3861E164CAB28CB8F006448139C8F1740FFB8E7AA9E5232DC1B0000000450E78E70021A0002AEB5075820849385BD8ECC595A27EF52DD145ABA067166FC745C1D940DF3D258605FEB79DA82A100818258209DD11E64A93710854FEDF85F1B4B23192079B812B67AC3EE0B97BDB1111380615840B41A3539134FB3F8D48F75C876830777CCD6043E9F85D8A2477DA041C5160BE2497BA851CFB19D30DEDEDB9C767CBD8063E1ABC9278A3AA400427A81F09FC709A1008182582073FEA80D424276AD0978D4FE5310E8BC2D485F5F6BB3BF87612989F112AD5A7D58407910E0CEDB222E4018BC1F11EA4D4EBEC6811A1B436610470E2377C6DE4EA28DF66E657A97E62B52964AC9F17558865D125FE46379576AC2544F05BA4ADD300DA200A11907C1A363636F3281A266736F75726365664D485A3031396576616C7565633438336868756D696469747981A266736F757263656553485433316576616C75656439372E326B74656D706572617475726581A266736F757263656553485433316576616C756563392E3401A11907C1A56641444142544381A266736F7572636569636F696E4765636B6F6576616C75656A302E30303030323937346641444145555281A266736F7572636569636F696E4765636B6F6576616C756563312E35664144414A505981A266736F7572636569636F696E4765636B6F6576616C7565663139342E30346641444155534481A266736F7572636569636F696E4765636B6F6576616C756564312E3639674254434449464681A266736F75726365667472657A6F726576616C75657132323637343134383233333435332E313180" + // ; const TEST_BODY: &str = + // "820585828f1a002f48651a0295ebd258209b7738d2f814cd6d5ded17c2d470c91cca0a3fdd25655543793998c2cdbd881858208f3455035ef9178d72699fa60dadb1e0a7e26f2a17499074155b3c4cfcbfef1c58205251058d5f98cdd68cd920e2e169c3eb849405ca438b70889928ab29de85ce5b82584028ed7802b66c4f003171d06ff4a23a71f635383f1a92e18d7317467274311f1a0df7234e07c608102f144171670cf140382b5303f48b80f1fa8b42fce686942b58501293eab0b20cfc34caed068c0011acea3c4d80e0021036bfededb502d79ff2ed885ef02108f7191ef169c3f5f8521e5f8dfa56c4b9b1d4029af63d43368ab8ddd59f99fa2260ae3b4bcb641be8a1900e825840000a6aa6b202e15ffd14a9058409bbd03443d8996323104133877e57622f290a2490544c50960a51758cc251fdc7de80a3298c607b8648d239487b1d29ee792d5850dd5f692fd4bc313282500acf5c765443930032655956710a28298e06883f702dee01ec51710b2ed1a061d422b64269427147eb22c40fb5b4b5ae407a8ddc812f0427eed2717c062975448e90c4876b01190b3658206541929d5ac907fe28a646eee3bc16fdf882292c179393447209f5e0df0c0d1c58203895e87ee01d33e288a4ff5cd00ae36b15aa9e8b29cc9c372d0a10837345b49809190134584091847ea1dc8397fcd79eb5378680ac884fd56efa7ef5f002676ab8d8c7ac16208c2792391099b12d17a24d22a4f9daecd865f678ea2cd305941a1ca94257210a06005901c0bbff78a1a0e725ffc15a241c77543e8126002066e5c8377346d725bf210c6883da08e0c4a0f6c56afb8db5408114f0905cb69b9200456eaea9ce25060470060484cc673a4294c20ec39d8384374f948279b2a61f421b98b09bcbaf7bdea18010a5b9c6a8881f7ac0263305f25f25e39dcb168b1729a3e8398ffe894475a7870d6fec76d1369bfd70349af2cf58849ca2f5f3d85ef39c33d9b1b91ab4bffef0e78f7672f301e02940f149465ee6e7d29cc5dc39e80aa666e8e1534798d0843c40233d79b5e7f203dd3f81274762933c1e80d8efe12e2bbdebccc0bf7d23e95445dd42c5077f29ff7efe8d62a7ddb88db4312f1c10c3beb25c2e8374ea67219af3ba4ed6a629a99dd0a94a6d73ebc3ec8f90826bfad87e191865a7a50ef75631536a979edf105aeaf87bdb13c0fc445020983433b250ad8ae7e2161651f664c77e8bf5364a37674a234875f005dea3c2723ec6c1d5133d01d814c47efec5fcd5136df4184439b904d8527d1c034fa62b3e86c7cfd83992b7a9ee203400cf0c8584bdcea505de9c913d92c660135749ca91254da93a1a5f7365afc061b21b3aa59283bac1a4d233142454fe3293ea1628c3e353d731e62ea57f93f11f264e5f5d5486a60081825820717c8f6669f2a23d9496cbafa9ad971d133da4bde9351320f31ead9e655af1ec01018382581d6073c79da74be41b27d2615aa6a37aae7b9ed198db98180734b787447c821a001e8480a1581ccfc88d2da7673f2f29a63167839bd100a79091826d7106b171010fb2a1524d495448554e4a4e465431757064617465640182581d604cb068cb16029a08b7c22826d26891e76ecfed27149a093d71ea10b01a3b1a568a8258390007b5bbfad7192d21b5ba5dab97550788f3624d84e53749d859923e79a4c3864ff1f301229f7327ed862c1742fda268acface349c5a71e77e1a001e8480021a0003330d031a0295fef407582005ee0d396af53845c6544c7d79b2af0202f30213ae3acf9a1956f2c2b838e29e09a1581ccfc88d2da7673f2f29a63167839bd100a79091826d7106b171010fb2a1524d495448554e4a4e4654317570646174656401a300828258209c1fd1890883594acf5aa650dcb3fb5575ae36dc716d65b7d35085226850a98201825820d86023cc0d6aaa5369ce5ed2f0b58cbab1a4a342dcaec3d0d3eb358770ccfc1c01018283581d70e1aa22984b9d5d1687d26f6a0f4ed33f106987107d7fe3acc345c0d2821a001e8480a1581c70a522742c69926a3e6da88afc76d3de6e1bda3ab9f1673bc6795b74a1434e4654015820a84c5151d5d712cb1e316a52ff79952adf66c15810db5a34a604ed047ce50c6e82583900f75dbc279ccbb34102e1db992d2a47c027da94d2fb88d561755e2ca7ff597e595e715fedd593998baa9af8574431cf129b089ab6dfcdf01c1a0197250b021a00029e35a4008182582039fd3ed2bbd79b2766c273e61aa9ace38e000eba137af488561da33e738fc2b5000181825839009493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc1b0000000450fa550b021a0002ae8907582058ba4cc38121377e3fd8fd562cad34713fce005dcbde64be58f08ea677b8c96da40081825820bb0368319dfb966ef87167ee46898457d858c51ada338796b13a57f255ee31460001818258390076b0d16f5d09ac02dd1786981066f6fedf7ac165a08b3b6f0fb33f039bf76872ce2fc9debc7c431ce4700ea060a5aefdec0a173d8ed6b4261a033f9abc021a00029d5907582067d342508ab82f0d87afebb26827fbc06ba2b58fe209a8d704008a9871a47baca300828258208a8bc249d91f6bae06b8825d5228d6a4ce9eaf550d2aced0c7d0eeb12b8dee1b018258204cc72591ad435591277f4b3f62fe7a6280eca05eee9e8be58e6040ad14d93ed801018283581d70e1aa22984b9d5d1687d26f6a0f4ed33f106987107d7fe3acc345c0d2821a001e8480a1581c70a522742c69926a3e6da88afc76d3de6e1bda3ab9f1673bc6795b74a143536561015820e44f754a48df3c04f5adcbbda56bca03b28af7270c5ee1878f783b3a42648f6882583900f75dbc279ccbb34102e1db992d2a47c027da94d2fb88d561755e2ca7ff597e595e715fedd593998baa9af8574431cf129b089ab6dfcdf01c1a001d014a021a00029e35a50081825820d282ef2c00a9975ac7c96ed24d906ade50951ad2fe6fba62e48aad7bebfda1e700018282583900863c2608950c5fc9b7fefa84e73abd6be60799818c0f4a39d7af7fb956d9af2dfb01a0ae7f34beeea07bd7999ca3a4b92e5e3b38541974a1821a057d341aa1581cbef1e9c947d3a407bbca55d21e019d528109223e5c0e43bf1fe777b2a24b4e6175676874794e465433014b4e6175676874794e46543501825839009cfa4dddd8b4fe587e9b73d535c9523cba7f84108f769b4e49223eb1bfda8211a59fca51637ed369096bd12322a9777be665124f41940a9f821a0016e360a1581cbef1e9c947d3a407bbca55d21e019d528109223e5c0e43bf1fe777b2a14b4e6175676874794e46543701021a0002c591031a0296127c080086a2008282582084a01a897c93f0274bc303673c254707f21a29e058308dc7ac5ce9d5fd69999758407b16314b62d263e17aa2f405fbd039e70f94afaa70cf2dd3af8107ce644753a88d5a73190d8e257f79610ed73825432327eb0308978cac488cad12366e0c3e0b825820cdeec72572a90f767a2103aba0a1777b11300fa2c63219324c7a9a2f56e3d2e25840d21876c97c6b2832a293960052d15df06efd068f7edc24f126ca19f35d3f3744e6c2f56571e2341733556f756d25b822a54f4016e5055c2bde8e01f10df2f60401818201818200581c1b20a61dd889f311a93e11fec20456389895fd8785d3b288bde84eb2a100818258206acd90e62a869f8dd75ce2626660583c73169fa11f0fd367f892847596a545a85840c25b1a4a9b10789f82c314b4f24008f6ded239beb883a29bf4abe608c48a1d9c83d3c3d448094640fefccc1ddd5b3f9c70c36aaaf35d396a6ed0ba7448ab7c08a1008182582073fea80d424276ad0978d4fe5310e8bc2d485f5f6bb3bf87612989f112ad5a7d584058096f18bde0125edd4cfaea75660f3988ad05f7d00f6e513896db2f9093f89d72ae7efc7eff220694153c5c9345e40dd55c2003d30aeb1c3fa9f3d747352803a100818258209dd11e64a93710854fedf85f1b4b23192079b812b67ac3ee0b97bdb111138061584027be9ea45e07d0bde54848617c75191f78a4ed0d17dafbb689513bffcb9b9439f7a5f1cd4d865cdc15415c18fadf8c4dee10b688f38ceffa24a22e8f56267e07a100818258206acd90e62a869f8dd75ce2626660583c73169fa11f0fd367f892847596a545a858402aa77032686d47e19bafeff34b028b25d9a326193497d23edc4044e5d3156aa70d04a391d8195db381e9cd2b60c67fb08f97aa73f1d27493bc9c9651b0c0b40ea100818258208b6fbde6ba7536626b336faf851d5014e09f1b97eceb22b5c4074d7fdba7784f58402a649935b8a50b02f4634ed5fc395dcc1de5521261b669ea815bc6c72dd677c391ee166586d7938b2b49b165d4b3436910e1c60c2a45fb59d219e04826927804a30082a11902d1a278386366633838643264613736373366326632396136333136373833396264313030613739303931383236643731303662313731303130666232a1724d495448554e4a4e46543175706461746564a56b6465736372697074696f6e782474686973206973206d79207365636f6e64206e6674207573696e6720706c617466726f6d6566696c657381a3696d656469615479706569696d6167652f706e67646e616d656c74686973206973204e465431637372637835697066733a2f2f516d55736f3368446e6434583134794d33353866563748346f715471686839737a3138733273483151684c56573565696d6167657835697066733a2f2f516d55736f3368446e6434583134794d33353866563748346f715471686839737a3138733273483151684c565735696d656469615479706569696d6167652f706e67646e616d656c74686973206973204e4654316776657273696f6e63312e308002a11907c1a56641444142544381a266736f7572636569636f696e4765636b6f6576616c75656a302e30303030323936396641444145555281a266736f7572636569636f696e4765636b6f6576616c756563312e35664144414a505981a266736f7572636569636f696e4765636b6f6576616c7565653139332e376641444155534481a266736f7572636569636f696e4765636b6f6576616c756564312e3638674254434449464681a266736f75726365667472657a6f726576616c75657132323637343134383233333435332e313103a11907c1a363636f3281a266736f75726365664d485a3031396576616c7565633438306868756d696469747981a266736f757263656553485433316576616c75656439372e316b74656d706572617475726581a266736f757263656553485433316576616c756563392e3480" + // ; + //const TEST_BODY: &str = + // "820585828f1a002f48a01a0295f35658209e7d7c1153f1053520ec5ddd5783c9abbce102a7ddaa6ad7b2f4d22b54b470a6582018968b08995cb3e970329170730bde3b963e0f62e7b230a2a6ceffff35abc1655820c971fa5ada9193edbc4d3aee97b9d128161211cef34f4eb0111093ea7924dae18258402e1fc5ac16e1720919efd6abd504f0a78fdff5ef6eab781f94d60809610ee83fbf9698dde10d978a6f2e1f71b4901cf3af3f02663335cc555ad09862bb3ae54a58505ec125dec3df7627369533888cfbade8f3e0aae164a80b613e98a36bb4c0d64a80f8c579d2036b652034a2665f8398c09cf0a3b6e81e5746ba8e1b73b03c7e97b1e44ff14d780fc187f5cb4f2de8c807825840002a1137e56b03f6115df82cd12ee998e70beb1d98db856e465f583dbf71b564279bf9d898dd4e60847be8de698502fd97c2a1c60c5416f697b2aa04a6beccb05850b23b27c316423e63de75900742ca6e47856cd82e8bdb2928e6f17bdbec08dc8523da214b99ca708258a7861238df0844eb38a2767429a61adc001566d9e41df1fec23b825ba6f77487d0bf77092ca70a191094582022bf0fb72c4ac84f89037aa744e776fe43a0f9804abec3eb905fc5caac3c8ab55820824b8822d5a267150ce11b114ca1ea152b08978130fad8e644df03cb16ca92ba061901485840f53189cd53d1c48deba8628941a22205dcf0745a630487e39b21391ebb9f6e73d0021b135a23f02d997a1fc5720fb6e63aee40d550be938bb99d4d532a33490706005901c076cb302384de7d9f9e994900373231b4f63c6c0c5a393272084d277230e88a5f0626e52655f8db35e63b0284612eedc546d3eb894e552346c9c787ad45c46303fb00ef5c772d32707fd92ccc4b21a780a4c3478e63f1cfc9e8b6ec68925940791d1c7e3436cdf95720a037900634296eb718442144340e0b19d3a80b01f86a34be6ee1689a915972c6036836c6963a4c74a20f19c02eb3ca9ebf31f1d89ad74faefb8003e6fa60d297c200aea07f84345ae19f3372cc4f2754548f4687098b529992a239f928c61b99deeac37c7bd2b687d0ed5880905a66affa56ac1a09ceadb28d87c3170e7b98798a7e3b584dd48826d65740c3a327eb2dd32999ddefba9bfdfcbde1b6046798ebc08f6c60a49b44b1a9900c50d62dfe40acae9090d93057a090a334ace22c00fe0235a80f2c77cd0cd580e1b9263e453bea4a0322e7f1b5aff9aa3383152bde5a576c9e6aa729151f5cd893278d97f120279b5a48c7a55c4a04e79f52416161c3354cb6fe90590ca8aff4374bef6fbff81eb362a8912a75c3deb4f786705a19218d259807b7b3c3fba05d1a35b88c0dd13cf2dac7ec36301cda9349f4983394504208a06e1a7dbe88b45ee547569b5c73dc7ea78b5506648ba400818258208cc730dab8e22eed4abb99431ac7b275c96abdb64440301d2a4394a1ab64e59b000181825839009493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc1b0000000450a1d1f2021a0002ae89075820a9ec7307739b1ce1c5e8c87a676d58f79e12ab0fb60fdd4854a208219c169355a50081825820f1a0e91f2f5b773ab3a4061da4d1c0c607ec5a38e2af29529f04b8b3e7715d6d0101818258390031e27b536478554a0ecfaa940d03c3a7fb712121e9e1b65ecbb2d852eacda39105223742da1efae9dfa82004b15b50a44efa6fea0c2c2dd51a02f320e1021a0002a725031a02960ec5048183028200581ceacda39105223742da1efae9dfa82004b15b50a44efa6fea0c2c2dd5581c5f5ed4eb2ba354ab2ad7c8859f3dacf93564637a105e80c8d8a7dc3ca400818258207ca4e57603b00b2bd661dae728aa46baaa069ec548f7edc9a020883a747811660001818258390076b0d16f5d09ac02dd1786981066f6fedf7ac165a08b3b6f0fb33f039bf76872ce2fc9debc7c431ce4700ea060a5aefdec0a173d8ed6b4261a02ebf5a0021a00029d5907582052ab5e2532f7d1c84a8f6264d770d045298a477c5bfff36a8cc7645faca6c674a7008382582068853450ded7c467f83e18b6a455e9c45c283c6d42089f798b3ecff98f5d42d20082582068853450ded7c467f83e18b6a455e9c45c283c6d42089f798b3ecff98f5d42d201825820c8d7a4e800d00f9fda815057c226187fba5c9d530583eb06486e3d9fd389e2ba000d800182825839007370aef4c65746337a9b1aff5bf71c510d589aef387ba9f2ce4936691a097fae4c554c571526dda43b031f9556cf5167613d63ac1541d1c31a000f424082583900fe565b49cd91b26dc586e9af1cbbc489fd8cd9b3b0e1aa24e7a2a9521a097fae4c554c571526dda43b031f9556cf5167613d63ac1541d1c31a001e4638021a0002e951031a02960f240e800758208eca0841a79436578f9d579f28de834d0bf38272fe45229bc3af81ec25c3454ca600818258208cdd88042ddb6c800714fb1469fb1a1a93152aae3c87a81f2a3016f2ee5c664a010d80018282581d6095ca37fc71b3b73f2d0e57258ac66857661f33a509436c10917aaf6b1a0022e4be82583900f3a7ac77147d69c04060dcdc48b51271b3641f0c044ec4acb260af29719b6905b4122f96283e50a0dad9a9e577553ad49a17bbec3cb715181a2dc9876d021a00028f6d031a02960f2e0e80a30081825820cbf75d20e64f3e6ad301aa14906dbd8b2152993990d139b978ec4f451a739d3a01018282583900736fa1025c0a1d1966f4eeaefa6669d70f9c04c4c650286530d0bb5dd723da7bf8787357a74b3fa486b13462578ecf6ab6774dd4f15043681a1d34ce8082581d607c4a71a51d0c7400a15a748fa2338f20c8386f1ed4ebc56c2ffec4b01a00933c2e021a0002ad29a700818258204e66af4f8a441493809c8b0b2c4bb692fa70267fe2796c857dc892029f09abd5010d800182825839001c541902877bc024314618217cff3426ae99155377aad979122ac60ceacda39105223742da1efae9dfa82004b15b50a44efa6fea0c2c2dd5821a004c4b40a2581cee1d198fd1b4ef0f8aa0238d19c55c22562252e46abe195e79f68762a14e53696e67756c61726974794e455401581cf5bb6672a26bdd5a956da1f09405181aedf10e640e01f87da06a4809a146676b636f696e0182583900f831861a4cb622e02a71d4d38285aacd928c4829686ee8c311ae6f012902a312156562e9c98cc869b6c8e5356d7b97cc3ea7398839a423e5821a015f76c0a5581c21c3e7f6f954e606fe90017628b048a0067b561a4f6e2aa0e1aa6131a14756616375756d7304581c65b2b0dd9028c871ac1997399979cfc12a7f05738e1ec8d8a16ea52fa15041616461476f6c64656e5469636b657401581c789ef8ae89617f34c07f7f6a12e4d65146f958c0bc15a97b4ff169f1a14874727565636f696e01581cdca984a3ab2c7b9691de0cf0e09227e19c06b2899500ed8e9279ab32a14001581cee1d198fd1b4ef0f8aa0238d19c55c22562252e46abe195e79f68762a14e53696e67756c61726974794e455401021a0002eb8d031a02960f4b05a1581de02902a312156562e9c98cc869b6c8e5356d7b97cc3ea7398839a423e51985590e80a6008182582094b342f0321154684f60cd01db8ed3744e7d3351cacb61f871058c0021e57b0f000d80018182583900a34918d5acc448cf11512bc4a9e62c245a5374971bba698f87787882579b8fbb80215c823394921c8c4a1bdc1775224446f98b266a92ba711a009354c2021a0002a885031a029600b50e80a500828258208d134553eb77b0fe32c09dcecab66f59f57d87fe766b64ee05a5775443ee994800825820b6f47b1ad870ec0eb9948f2b6f86bc5893e868fa1bc109d7bfe0f1d07038f5b6000d80018282581d60945c010ec1c1f884ed778c28b3c644dcc2da1c3b1df4a90924cc51de1a3aae2ef983581d70396c6cf112a57e057331bd72a9de740a9431d9f10a4bd0fd932472e71a0096026e58209ad30ffde0d1931ed4f145fa0a0d320a067051bfab1b08cbdb79e9f26df55df3021a0002a4bd0e80a30081825820b60d4a2d2b6de3c800c8700e3ffce731c9ac62e7347986d4a2e29ba41be7545b010182825839005a018206e2e47778d44f42548047555389504d42437d7e85839c604209d5fa01ce1155980099c5c28838cc74c7c61ea8997c77a22d95e8aa1a000f4240825839005a018206e2e47778d44f42548047555389504d42437d7e85839c604209d5fa01ce1155980099c5c28838cc74c7c61ea8997c77a22d95e8aa1a0058b481021a00029049a400828258200572697b862ba97fb2cdbd6e39de31bb15f57cf086a03092e3b0ef58f63568910182582043549de56f2a03b3471bd525a5d4684969c02d5d5396a575429dbabfc51c5f4801018282581d605d91fd94e2f6de8646b5f0bf2920fd8345db49168fd18ccca80215471a00989680825839008fc9dd9236b232fb122ca3213a4a2a56beac5153e7803223d705fc62326c990a273d2279ee1390b79423e134e648e791938dcde0dddf732b1a38c5d22b021a000292dd031a02960ec58ba1008182582073fea80d424276ad0978d4fe5310e8bc2d485f5f6bb3bf87612989f112ad5a7d5840df40fb1382cbb46728313ffdb5db2835dcce37ec6295ff0948619ccc33b7b6dc2c425b72a7f92ebfc2abd2a2e67e1f020040d10155d847d671fcd8aa13918208a1008282582057b20c65f5f4ca24a4c86de3379ce811f416b313019ff306d2cac31f86bd00e1584097d965e9eac9f986c4a1b65a9ecc83258565a415ca0fe5e376a87ada105e126ee7091e3cf6bb21436ad84d25b0671a55121ec593063d0b1f57a9a46e0a601d02825820cc42fcbb811f0415074548124dcdb27769cd912ab38201d38b22cb79e7d8299158408000f7179bd3018580f88cc66948a871046a8bbe125f3d449342cf65187665104a5a6e7029a48f3b733a74b2dd8af825244f9840ca71cee4c7144ec33bda9d0ea100818258209dd11e64a93710854fedf85f1b4b23192079b812b67ac3ee0b97bdb11113806158404f2c44294027640887820ac5145169920eb23c365686cf7424bc725d301702d15768de1f88556fbcb328f93c1ab106bb8573958e3d7f5fd4799c3a9912f49f05a100828258200726b0f1e20bba8b62d6ac14c7d3baf73f5bb5ca7325f7ca64a9c7f6c6c1df3558400ba2ca792d7376400bd3bc11c0eec4f7d7bdd0e16777c1642307ba3487eee1dfbed839e9bb35439f0676d3f95d37990e087b5a6043c461d2637bf0827f46030c825820a2c791779639d2702328025ed4b14df881fe3d787d557a5c80c9065498358874584011fc26df5e04c3c533f726d41d5fa93a6cd0e621e81cdc426affd74d908ae86391b475a57b77a1385c96b9f8099bb2c4173dac4a576953f60bcdc77ca8af0804a100818258209ff16abb82eb7a412d745a0067e67115d01403fa27985aac74c2576b4a3d7f9d5840dba0bd58a1e738ad8d710e1d93a3011034dde9de05e9b2bf4c6eaff5ecb02f5b5c577686be5395caa4505f72d574fbfcb1061384b6b4b0ce66578e109f1d090fa10081825820a87e24f3f590c98c9bc1f34e11607db0e04446afb3b02f3201b8a6972e50512d5840e1dbb81e62b73dae6e9dd89f43f8160eb17d29201ce41b3fc57a06e641fd04ce44b257ba72d0092cc8af5bc84a2b0fc1d9e91c0273e04c9beb135b376e34e900a100828258203238eab147de641e6622ef8c246c226e744ebbf0c9fc9db298588284719a56865840db728fc9eba7c9da33938b480516458c43f17d5c9edf2beaa27d08430f5d3c1c125e4a1825f4bb568299c28229692efcc8b8bab870224c5f39f1930151f3b003825820750bf509b6b984928302eee7535f89f2753fb553a825148bdbcd9d9e6087fb3258409d9cd0f3be0539e726b543112bc21c27d9095a02b391458d2c94ef55b4a04d63b970bae85b0d1304db46fa3777320a6a15231387ec7091b720f8d4615ff75405a100818258200843a5a078c1e763805be18de9c749bb163c6cf3e8ed07d9adc37eadc5e7fd8a58407a82f27445d8045a02a64265fe2cdabb5c268814c002b827b077264d52c5c346c01cd43558a30eb718ca17be97e23d3db5fdd2deb34204f7b6dd13f047409505a10081825820a685a3def8a62b3c2e0886d00b989e1cb2a58a8e733d3a3b1dcc88646d093ee9584084815079efb0fbffa9d62256d720e538d6efb331cc7dd7575e75f5b41aae3dbb6b9150cb0f0b256caba4825e7285c142c01f9376bee17395bd58581daafb2c0aa100818258206e38402e45654fe6f90c992568563993df0f9d0bc6e3a216647b01bc68529894584044abf4e6de63104443c7a63643893ee3d9b98c3a8592e10d9cf7f16c3107243ef197df9ab575c893fc421906f2b500a0f6d798718e080206d8c2adf64b1c680ca1008182582037952999164b486ec64f2e389161e66c3c2061374df660f4c342d91678c035d35840659c17e6f9c2247f35ab0f24e4c726c5f9a1b43092556cd171ee0bd699d839b06e183aebf360b202fdc458f839a65b5f6099d5682bcee7034d8dc65837231201a300a11907c1a56641444142544381a266736f7572636569636f696e4765636b6f6576616c75656a302e30303030323936396641444145555281a266736f7572636569636f696e4765636b6f6576616c756563312e35664144414a505981a266736f7572636569636f696e4765636b6f6576616c7565653139332e366641444155534481a266736f7572636569636f696e4765636b6f6576616c756564312e3638674254434449464681a266736f75726365667472657a6f726576616c75657132323637343134383233333435332e313102a11907c1a363636f3281a266736f75726365664d485a3031396576616c7565633438356868756d696469747981a266736f757263656553485433316576616c75656439362e396b74656d706572617475726581a266736f757263656553485433316576616c756563392e3403d90103a100a119534da261760161638358401001229d010a03312e301295010a076d6173746572301247304502205098c7f0a34ffcbc6f5eea8e6cf68b80370fb8200227d885405fff3cedc5b3da022100a7584089ffddf74b658b31124cbc5850a52a638a6519ec07f79e0bae0a443acfdb9f1a410a3f0a3d123b0a076d61737465723010014a2e0a09736563703235366b311258222103ed5ff07ccb9402423bb1459646e447d1bd4c75d0c560b0a4afc4909166d90cec80" + // ; + const TEST_BODY: &str = "820585828f1a002f49f11a02961d1258208625fe26bc3dfbcecf420842a842bec9e1305fac4f28c8e2f37e8ba6581b29f05820c0e8222276d2ef82720ba25604295e72bc4a316146dda5b58d106d40d81695c958204aa65daf374ddff0977ba493e27fb798b446443058c0614a139a14c1a15572158258401198dafd503de57628945f8edb8115973c96f69cf9ed87f32d34450baf939f4a30abf357d8f723e54e836e8e9cc8708dae74cdd312cb622e514f6ffc21175f2258502a7b9994f69338e399134b8f9a6ab04d38ba423b45c24307236dfe43d12e68004aee90b9debada1b75bc25ae30230759043687212df1bd9a0bf566f99ff6fae40cd0ea849fa9bd3dc8dc1808a834be068258400000a06943c019059b8f276a9195cbac0725e2cf4506c254a67bd9d4e6e6cb2f03674d914c0f12f45382c7df5ab0d48cc1a99bd9b7f38a9a257a4f532ea2bcca5850aa66308381b70cfe744a1ee868b130c559c98379ad026bd6a4d464519e0dcd6d637e788eae4e79e92a1eff593c53ad46bbd0ca7700de813657503195a7ea29d6393ec8b26b127f1541e1087e870df00f190b88582066f00f57f5e9f2b9fec650f977ac61100521394727e11724d6639383b18cfddf58207ce755b43a53031b2e18adc034bf16cdf86fd4b0e280bf5fbcfacc8c31c916d005190134584053e0400bb413146b38b388a820f6b82723d74424962671b9fbc64e29cbdcbce7d1b65e1c38ae94f54d574d1fc9e05fdb2954d8ec48fd9aeb9174329bad48f80906005901c0968c11bbaae36df33471b0a8593fce483b326e218a3ac7f3f941fff6c128b3344f6047e7dc4ace6bec5e8b403189e3c632df0243b37646959c86e53d71b9f9026efc27474ab1c9222090330392fab9545ace4203e8e5b5815fc546d9726a3551748199310f38c82733d2c26a6eca9c455faad8490ad1117b82067f18247e733c48eb81f4f84ea91e2f8570a1e14ab6585c3ea81715482aae52ef28b8327216daa3335f88278fc3c5055c2b292d2f77af1d25c82890fb7d0c1c3974bcf30caa772405051c6c051ff36d5d4c1fdd661932aafc94d338b476751fbd938fa55868f45a43c643f2965d708313bdecdafdf1f467ed6e01edc770128304a4a5b12729f52f03dc1c4442eceb010261c8b13453816fa9e74adda29046cb9d5d19e41f7171cf4d32773b0df915b3c1dce5beca60bc65429f61410ea3c1382c8ae3992ae3051eda2a0dabb09d9d7c1c342be2818dcfad3b5f80672a3824540d4bc80e6aabc194d42f50b20633f9cb8f244cf6bd8781f16d71e09e9753b3add529dfd6fa8efd5131dd020216d4e1202d520dd5e2fc1c36729531e752c16e597a701668db814fafb003602f94e80c05de55d43212a8ab0914cb011ca570ee53fc40cf4d87b9cb87a60081825820fdd98cfa926529606b9fe2157a191a6a6ce727b96c73ef5032e510b4691ca71e010d80018282581d60840fea336b42703516ed3c7b9b41eae0a2be502db73af3a4a3b254ba1a0098968082583900be06ef0f986cd131166fc463984c7cb98ccc061bcf80a4a18c3dca5bc34679a0b5f341ff784f79a3aa86c92e4cd1faac06b853d823bb0f2d1a3a647e26021a00028f6d031a029638e20e80a40081825820e3a56dad9c47d1cc39663a00a408053fdb18472d6e46c103060552ece0f9ec1f0001818258390076b0d16f5d09ac02dd1786981066f6fedf7ac165a08b3b6f0fb33f039bf76872ce2fc9debc7c431ce4700ea060a5aefdec0a173d8ed6b4261a00ee60c9021a00029d59075820f2aba5f8dce02f91959bf66a32cfb4967119a6e83caba1675354b0f61f1e03e1a40081825820cc4da7788291fb5bda0fb83170f86b992c05fa37f10d4c7c917da76bf7d860ef000181825839009493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc1b000000044e8bf9cb021a0002aee10758202e47ee579cfe227820acb501d3092b71083df25ebbe443e93ec921fd3de010efa30081825820075906a8cea2f77bed033482e1fb005529315616904bf073a2daf3d7d5c48b5b01018283581d70335a82969b5bb6147f6adcab3d933b55ddd43c92e47decbc7f5eba661a0016e3605820f0bfb1af4b8c8aebe45307269f1c14a4a26995eaa9c0dfb188714491d8e44883825839001ccda49705cc7fca4b045ebf4c5c86b87910e0bc2d43a21b85f58ce6dd281dff622d735a69da8b141c3911fab6672ae220b72e277a14b2a7821a01d8f548a4581c252394c48330c6d6ba5956411eacd8435544d4116e2066f5210ff4bfa1447465737401581c6f907e5a0d2210834a6cdbeefec7926795970554585a913953c8e674a1447465737401581c8a1712b0a91b1f59b2efab8770542a5d5b1cfd33f83af46f5376b4e8a15346616b6543617264616e6f426974733437323001581c8e58635cbf59622d4a7a79a9bec3e218db28044865356cef284d45cda14e4c6f72644279726f6e433130333301021a0002af91a50081825820ab1fbf8d3e6f95d6d4569242cc51591c3edd02664c4dfb34db261be3b047db23000d80018283581d707e53f44aee9de0f0bc82bdfa5bc7708b523d4ed9d2b5814022a5b5a51a009896805820923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec8258390089a267137d6146caa5da09d35ea0d392ae8cc16ec7dbb3e5e386cb0facbf92455bc1e79b0fec250dac2434e423d366e4c1005c8f9bd4fe141a3affa22f021a000291510e80a40081825820d9ad3802cb03910a083fcd4cb8bb7b74946db6a8f94d2e9dae4ab5e67d6ea7060101828258390017ad91a87733b817b8fb98bbf26b23ef02f6b35593c53d402986d58e32ae37093a67c37d6c4c8240fa0e3cbfa390ca7ce2c99d4692b14b85821a0014851ea1581c6cc71ef32ef3ba487f075c1437b5a66404fa5e32c90d4771db88efaea14874657374666972651927108258390065645a7ef481423df8ea5421f7df989b2baff43f8fc65d93736b7a9187c52f925f00774ed681b53046e180a04fe04c569af137663e42c92e821a076aeb58a1581c6cc71ef32ef3ba487f075c1437b5a66404fa5e32c90d4771db88efaea14874657374666972651a00153f61021a0002a071031a029620a2a700818258207497675581872f9d6fc34eabd1a42487ea95538bd2993c349d9e82f147ef1b26000d80018182583900065c5205593b8796558d5693eaef45ca92f70dacc7ffcc005dc744e39bec88f3ab280470a2015aac2d90a8e28561928a60b92a6594ebb53c1a3b97ed67021a0002dc99031a0296203904828a03581cd15ffafd9926e1f8f5359d264f2116c3025db67fd0080ec22339296d58206b9b0891f314d2bff7c86ed00e677ce4c91cc65682ac9f23cba9297072f064771b0000001176592e001a1443fd00d81e820114581de09bec88f3ab280470a2015aac2d90a8e28561928a60b92a6594ebb53c81581c9bec88f3ab280470a2015aac2d90a8e28561928a60b92a6594ebb53c8284001917704447f4a4cdf684001917714447f4a4cdf682781c68747470733a2f2f74696e7975726c2e636f6d2f353872327772763258209a594651acc7c102fb98ee9336069552ffe0720d8a6c8f4a0d21c4ed43c1fbfd83028200581c9bec88f3ab280470a2015aac2d90a8e28561928a60b92a6594ebb53c581cd15ffafd9926e1f8f5359d264f2116c3025db67fd0080ec22339296d0e8087a1008182582084834e0f88170f561f39de65e3a063938fefa17c1e48d22037fb5ef73dc36fb05840c1436df33f721f26149226343d714ec1ea5683d75637a2fcf4dd968aa92ed77cc1a30479ebeb08aecd90cf6eb14af172a6fed9f18ef4cf4386dcaf96b4a8950fa100818258209dd11e64a93710854fedf85f1b4b23192079b812b67ac3ee0b97bdb1111380615840c3249e7d4f92d68b26c3669e4323ab163867c3324dbf01f76e468d8ad3d1af509a77d7d27adbb5080cfe5f72e0fb048744505438e2df282c95777931d0d1c70ea1008182582073fea80d424276ad0978d4fe5310e8bc2d485f5f6bb3bf87612989f112ad5a7d58409ccfab2aad1165c78f2feddc807814e418f78ee08d6881b4c5369f7dc26003b954ab22304dbeba3f3c73e68eeb917fc94bce42ab15932bfea99b678caf4dd90ca100818258207046c06dee5f2bf959575f97b753c6724cca4ed69c29c83dc00598ab3b20dac6584006d9fafbb13d77262d4f6e36eb7249a77cc3188485cfc843eb23d17a1dad751b4268a24f249792c6b92f8d5cf80a7553fca1c50a951ac66eb78e251a70512602a10081825820c2923fc4c9b2a917413a0634e15be376832ee9274f87f40235a976503f26d03458401324ae6e48d914f4b8bf05b1516d461fb973e8cac8f15ff21de8977d8889a29e59259cf9e8a2b64b085f139942c0948861fa756fda2cbd810e81f5c51856d00aa10081825820ba8104249b5968ff681edb118f536ca7ebee340b88755c02a3d392763549687a584093fd3a634ed0edc523982afdc9adca6b6ef2222a7034be38c3c48616008a8ef58c99f4966633d61746c77f124a1b64b40ebc19f23b4018c2008c60dae1bfec0aa1008382582045e178eafb75d7ea928f6581220442b07c62d099791133aa7692d1bc41a96d5058406741fdd0822a0da3f0e09d88113e82be0083a6028a2049c578711aa9c694448f27f33f9737c4569ed660f20231c6e3021cb4a97fa8556fadff563867ac2df806825820e97f03551fd1ada110c60240827c425133c15bec3352a58033e4c61bbd3f906e58404b9eebed98c1fd8284a39a80af2acc84a6685ebfbafa9b15e9f8d782f6d2535df483420c1cf2ad42c5e96dc5e1a1f20451d5b667cbe1bdb7b1e5d90326a3730c825820be317f81ebbe422117ffa84edf4ac779824683771c2c30774e02285551434a675840302e7a7e4ba12cf462896dae66a9c7491b882559424f0dc0e764b28ffc086ceaf1df52a369cb4d2c5ea59c55ea12104db90255f07e7b99ec32b8242b5e327d0aa201a11907c1a363636f3281a266736f75726365664d485a3031396576616c7565633437326868756d696469747981a266736f757263656553485433316576616c75656439382e316b74656d706572617475726581a266736f757263656553485433316576616c756563392e3502a11907c1a56641444142544381a266736f7572636569636f696e4765636b6f6576616c75656a302e30303030323932386641444145555281a266736f7572636569636f696e4765636b6f6576616c756564312e3438664144414a505981a266736f7572636569636f696e4765636b6f6576616c7565663139302e35336641444155534481a266736f7572636569636f696e4765636b6f6576616c756564312e3635674254434449464681a266736f75726365667472657a6f726576616c75657132323637343134383233333435332e313180"; + const TEST_HEADER: &str = "828F1A006215E21A029D10A85820FF8D558A3D5A0E058BEB3D94D26A567F75CD7D09FF5485AA0D0EBC38B61378D458207A353CDFA0A5B5471BF05156097A4304DE6C9379E2937ACA6A00F954A2DC55CC582012DA72FCEAA4D03F6F409AF2AD2521C305082406B5AC24CBEE77CA3973B387BE825840988C010910CF4704EFF43B40F9B03868EF463546D942B8742BB4CFA78EB3E504BEAB8559D15F1D7F441BFED8265DACFCD96EE0D6744F08C4D4BFC07EF65A363D58508A66A6753E76E26285C30E18AC08EC683DE384A0EC710C94255A3392C1A500EB4A2737DE258640ADCB3B7CF98C431D2226DC5EFB8F61C941D5EA05469463396AF349FAB2AFF61E9FED64C5FE897651018258400000E31D3B969A44D385CFF79B9B0494675DF6A219438D551AD416BA2435216E3A712D3003EA97B638FEA979FA4405413694032760657D853882E8039071653F5850DCD355FC6F70D1D209EFC9120C9237A1F47EA3840473676766DA9867DF64058C7CB561466752D01E5439CBCA72EED20E042EFA9FE3167AD1A787D9D44A1219378F8BB1C62274C9246CF4BC99F4515304190A2F5820015D41B6EC5AC1DC88155B344E45B5A8D098C334CB997D3AC82A0571DAB5064558203BF4F4A527C0D227189F919893417AD461B8529E71BBD27E4ADE1EE20E8295F20519011558404F669665A14F8608608D848E3E8E61A9C17AFB55B4905C4ED3F3AC3152F0C8C9BA41ECE8607D240C33F5F2AE26FEFCF8422710E94261EA8152FBB7DC48BC0D0706005901C0B7F86863A0AA72E1693DC817A59F35104D5F26139CFAAB4F2EB7F39CA65B7BF31A0B52BA849DA4382369872E542585F153D888FA71799596E0CCF507074362066C4ED9F18E9F7D692487BC23679F561F5053A6AE0D79B3DA2C3C1DAF6FEAA8D8599417B409ABE17407EC339FEBFDBF4956C37925EE6448F44FE6AA0E715CC27E973CF28D21F1F5A40708BA7C229D282F152B95D6C7AEB9944E914FC68AFC1491B7DD883509A9D32F8B32FD078D1C0ACDEF06B4DF2908C07CC75405AE5B7C3829B92917E4B2A284F9ACEE94F2E17958AAC21EF0AC4E0D891081BCA96E0CE6907B5BEF9092340F5D6D9F249B02FC28024F7CF7560292B140FC656AD70BF915B4739092FF52CE23721E12340416AA9A22CC53801D98FB283873936609DFAA9A7F16028A2CE69FDF40F905E18DFE3148BB7B0B912B0FACD2F73B56E2EA7288C877356F36F0CFE2C0DD45A1C0C41F461A2AF154853F309BA97D1CF3AF539DDCB457DEE6032F2CE5E44638B56A60391DFBC50C3F05D86690B5B652FB45AC7ED62B06099B54257A6E0BDB91164172D4635BF2F3ABE85015414FAC2E7CBED797DF9211531422CA3F66B24FBE71A970649CCDD77DFF498AF535E38B7365A5DF0F9C12836C"; + + #[test] + fn block_decode_works() { + let bytes = hex::decode(TEST_BODY).unwrap(); + let block = Block::try_from(&bytes[..]).unwrap(); + //println!("{:?}", hex::encode(block.header)); + println!("{:?}", block); + //assert_eq!(block.header, Header {}); + } + + #[test] + fn header_decode_works() { + let bytes = hex::decode(TEST_HEADER).unwrap(); + let decoded = Header::try_from(&bytes[..]).unwrap(); + + assert_eq!(decoded.header_body.block_number, 6428130); + assert_eq!(decoded.header_body.slot, 43847848); + assert_eq!(decoded.header_body.protocol_version_major, 6); + assert_eq!(decoded.header_body.protocol_version_minor, 0); + } +}