diff --git a/Cargo.lock b/Cargo.lock index aa7fea01..fce2afa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1065,6 +1065,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "const-hex" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.7.1" @@ -2052,6 +2065,12 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "histogram" version = "0.6.9" @@ -3056,6 +3075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3139,8 +3159,16 @@ name = "old-faithful-proto" version = "0.1.0" dependencies = [ "anyhow", + "bincode", + "const-hex", + "prost 0.11.9", "prost 0.12.6", "protobuf-src", + "serde", + "serde_json", + "solana-sdk", + "solana-storage-proto", + "solana-transaction-status", "tonic 0.11.0", "tonic-build 0.11.0", ] @@ -3599,6 +3627,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bitflags 2.4.2", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -3863,6 +3907,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand_xoshiro" version = "0.6.0" @@ -6824,6 +6877,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.7.0" diff --git a/Cargo.toml b/Cargo.toml index 12ad32b6..b31b4905 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ bs58 = "0.5.0" cbor = "0.4.1" cid = "0.11.0" colored = "2.1.0" +const-hex = "1.12.0" crc = "3.0.1" crossbeam-channel = "0.5.8" fnv = "1.0.7" diff --git a/old-faithful-proto/Cargo.toml b/old-faithful-proto/Cargo.toml index 07605d72..f9085e3c 100644 --- a/old-faithful-proto/Cargo.toml +++ b/old-faithful-proto/Cargo.toml @@ -5,9 +5,20 @@ edition = "2021" publish = false [dependencies] +anyhow = { workspace = true } +bincode = { workspace = true } prost = { workspace = true } +prost_011 = { workspace = true } +serde = { workspace = true } +solana-sdk = { workspace = true } +solana-storage-proto = { workspace = true } +solana-transaction-status = { workspace = true } tonic = { workspace = true } +[dev-dependencies] +const-hex = { workspace = true } +serde_json = { workspace = true } + [build-dependencies] anyhow = { workspace = true } protobuf-src = { workspace = true } diff --git a/old-faithful-proto/src/lib.rs b/old-faithful-proto/src/lib.rs index 3c095021..419ecf59 100644 --- a/old-faithful-proto/src/lib.rs +++ b/old-faithful-proto/src/lib.rs @@ -3,3 +3,157 @@ pub use {prost, tonic}; pub mod proto { tonic::include_proto!("old_faithful"); } + +pub mod decode { + use { + super::{ + proto, + solana::{StoredConfirmedBlockRewards, StoredConfirmedBlockTransactionStatusMeta}, + }, + anyhow::Context, + prost_011::Message, + solana_sdk::{ + hash::{Hash, HASH_BYTES}, + transaction::VersionedTransaction, + }, + solana_storage_proto::convert::generated, + solana_transaction_status::{ + ConfirmedBlock, ConfirmedTransactionWithStatusMeta, Reward, TransactionStatusMeta, + TransactionWithStatusMeta, VersionedTransactionWithStatusMeta, + }, + }; + + pub fn confirmed_block(block: &proto::BlockResponse) -> anyhow::Result { + let previous_blockhash = <[u8; HASH_BYTES]>::try_from(block.previous_blockhash.clone()) + .map_err(|_error| anyhow::anyhow!("failed to decode previous_blockhash"))?; + let blockhash = <[u8; HASH_BYTES]>::try_from(block.blockhash.clone()) + .map_err(|_error| anyhow::anyhow!("failed to decode blockhash"))?; + + let rewards: Vec = match generated::Rewards::decode(block.rewards.as_ref()) { + Ok(rewards) => rewards.into(), + Err(_error) => bincode::deserialize::(&block.rewards) + .context("failed to decode bincode of Vec")? + .into_iter() + .map(Into::into) + .collect(), + }; + + Ok(ConfirmedBlock { + previous_blockhash: Hash::new_from_array(previous_blockhash).to_string(), + blockhash: Hash::new_from_array(blockhash).to_string(), + parent_slot: block.parent_slot, + transactions: block + .transactions + .iter() + .map(versioned_transaction) + .collect::, _>>()?, + rewards, + block_time: Some(block.block_time), + block_height: Some(block.block_height), + }) + } + + pub fn confirmed_transaction( + tx: &proto::TransactionResponse, + ) -> anyhow::Result { + versioned_transaction( + tx.transaction + .as_ref() + .ok_or_else(|| anyhow::anyhow!("failed to get Transaction"))?, + ) + .map(|tx_with_meta| ConfirmedTransactionWithStatusMeta { + slot: tx.slot, + tx_with_meta, + block_time: Some(tx.block_time), + }) + } + + pub fn versioned_transaction( + tx: &proto::Transaction, + ) -> anyhow::Result { + let transaction: VersionedTransaction = bincode::deserialize(&tx.transaction) + .context("failed to decode VersionedTransaction")?; + + let meta: TransactionStatusMeta = + match generated::TransactionStatusMeta::decode(tx.meta.as_ref()) { + Ok(meta) => meta + .try_into() + .context("failed to decode protobuf of TransactionStatusMeta")?, + Err(_error) => { + bincode::deserialize::(&tx.meta) + .context("failed to decode bincode of TransactionStatusMeta")? + .into() + } + }; + + Ok(TransactionWithStatusMeta::Complete( + VersionedTransactionWithStatusMeta { transaction, meta }, + )) + } +} + +mod solana { + use { + serde::{Deserialize, Serialize}, + solana_sdk::{message::v0::LoadedAddresses, transaction::TransactionError}, + solana_transaction_status::{Reward, TransactionStatusMeta}, + }; + + #[derive(Serialize, Deserialize)] + pub struct StoredConfirmedBlockTransactionStatusMeta { + err: Option, + fee: u64, + pre_balances: Vec, + post_balances: Vec, + } + + impl From for TransactionStatusMeta { + fn from(value: StoredConfirmedBlockTransactionStatusMeta) -> Self { + let StoredConfirmedBlockTransactionStatusMeta { + err, + fee, + pre_balances, + post_balances, + } = value; + let status = match &err { + None => Ok(()), + Some(err) => Err(err.clone()), + }; + Self { + status, + fee, + pre_balances, + post_balances, + inner_instructions: None, + log_messages: None, + pre_token_balances: None, + post_token_balances: None, + rewards: None, + loaded_addresses: LoadedAddresses::default(), + return_data: None, + compute_units_consumed: None, + } + } + } + + pub type StoredConfirmedBlockRewards = Vec; + + #[derive(Serialize, Deserialize)] + pub struct StoredConfirmedBlockReward { + pubkey: String, + lamports: i64, + } + + impl From for Reward { + fn from(value: StoredConfirmedBlockReward) -> Self { + let StoredConfirmedBlockReward { pubkey, lamports } = value; + Self { + pubkey, + lamports, + post_balance: 0, + reward_type: None, + commission: None, + } + } + } +} diff --git a/old-faithful-proto/tests/decode.rs b/old-faithful-proto/tests/decode.rs new file mode 100644 index 00000000..fc028947 --- /dev/null +++ b/old-faithful-proto/tests/decode.rs @@ -0,0 +1,117 @@ +use { + old_faithful_proto::{decode, proto}, + serde::{ + de::{self, Deserializer}, + Deserialize, + }, + solana_sdk::clock::{Slot, UnixTimestamp}, +}; + +#[derive(Debug, Deserialize)] +struct FixtureItem { + name: String, + value: T, +} + +#[derive(Debug, Deserialize)] +struct FixtureConfirmedBlock { + #[serde(deserialize_with = "deserialize_hex")] + previous_blockhash: Vec, + #[serde(deserialize_with = "deserialize_hex")] + blockhash: Vec, + parent_slot: Slot, + slot: Slot, + block_time: UnixTimestamp, + block_height: Slot, + transactions: Vec, + #[serde(deserialize_with = "deserialize_hex")] + rewards: Vec, +} + +impl From for proto::BlockResponse { + fn from(data: FixtureConfirmedBlock) -> Self { + proto::BlockResponse { + previous_blockhash: data.previous_blockhash, + blockhash: data.blockhash, + parent_slot: data.parent_slot, + slot: data.slot, + block_time: data.block_time, + block_height: data.block_height, + transactions: data.transactions.into_iter().map(Into::into).collect(), + rewards: data.rewards, + } + } +} + +#[derive(Debug, Deserialize)] +struct FixtureConfirmedTransaction { + transaction: FixtureConfirmedTransactionInner, + slot: Slot, + block_time: UnixTimestamp, + index: Option, +} + +impl From for proto::TransactionResponse { + fn from(data: FixtureConfirmedTransaction) -> Self { + proto::TransactionResponse { + transaction: Some(data.transaction.into()), + slot: data.slot, + block_time: data.block_time, + index: data.index, + } + } +} + +#[derive(Debug, Deserialize)] +struct FixtureConfirmedTransactionInner { + #[serde(deserialize_with = "deserialize_hex")] + transaction: Vec, + #[serde(deserialize_with = "deserialize_hex")] + meta: Vec, + index: Option, +} + +impl From for proto::Transaction { + fn from(data: FixtureConfirmedTransactionInner) -> Self { + proto::Transaction { + transaction: data.transaction, + meta: data.meta, + index: data.index, + } + } +} + +fn deserialize_hex<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let input = String::deserialize(deserializer)?; + const_hex::decode(input) + .map_err(|error| de::Error::custom(format!("failed to decode hex: {error:?}"))) +} + +#[test] +fn confirmed_block() { + let items: Vec> = + serde_json::from_str(include_str!("decode_confirmed_block.json")) + .expect("invalid confirmed blocks"); + + for item in items { + let response: proto::BlockResponse = item.value.into(); + let result = decode::confirmed_block(&response); + assert!(result.is_ok(), "failed to decode {}", item.name); + } +} + +#[test] +fn confirmed_transaction() { + let items: Vec> = + serde_json::from_str(include_str!("decode_confirmed_transaction.json")) + .expect("invalid confirmed transactions"); + + for item in items { + let response: proto::TransactionResponse = item.value.into(); + let result = decode::confirmed_transaction(&response); + assert!(result.is_ok(), "failed to decode {}", item.name); + } +} diff --git a/old-faithful-proto/tests/decode_confirmed_block.json b/old-faithful-proto/tests/decode_confirmed_block.json new file mode 100644 index 00000000..1de54914 --- /dev/null +++ b/old-faithful-proto/tests/decode_confirmed_block.json @@ -0,0 +1,17 @@ +[{ + "name": "rewards-protobuf+tx-versioned+protobuf", + "value": { + "previous_blockhash": "0000000000000000000000000000000000000000000000000000000000000000", + "blockhash": "0000000000000000000000000000000000000000000000000000000000000000", + "parent_slot": 42, + "slot": 43, + "block_time": 42, + "block_height": 42, + "transactions": [{ + "transaction": "0199eebba8126354ea9913f40d3d5e5e0f027b3fa91fc794cdcb5b52978faff3adb2a6133cb985d062c7efeb10504cdcfcea02056254a4e898021343be1624ae0901000918d15f7fe461963ba26a3d4f595fcb1b75319c71714e7adafd05eb050db8b9168e0f9902b27c4dcbe1886026ea28b0f62a8e2a6f2fc2d6dc6039977d2659dc7b9b1711b21c8c13b92ead6d5a2443be061626f011d369c5137e94311325955b5a5918e9c4e91dadd620e64ad7fb58e681f582ac49c288bda541b7fa05de7e20592d1aeea271624534f0b40324926b14166ac85f435f996bff7b9ee3daff9ad70b7f3c5dcabdbb2d21b72d8538a3e0cf0ddcf709c98549d6e096dffe77e5d0a718893c97b7d1275147489f796be09686250eef836bda271815faf49b6486428f6edd4ac9fb73dceb9b7fa7d0c17bd5bcf4993db73eafd9f6d40f1c009998629aa5e960682dc41274e0a2bec3e6f855579ae0119159d342463c714532039f6270e5b56a2907a91a6addd1c4137cf5050558bba02a9d2746a6923b7b661d553c31d67281f73ef0e76451d812ca8b4842937674e1ee408557e0d975dd8e4e53fe432b87b0d93e3c5b35446308000a666c598f90c607314a665dc195296535d75af1664ada4f06fab5fbcde8dee7921baa861c07c24e47ea148a64e4328c9ea74ca68a83eb956f0b680bfbe5f65ba8bb0060ff311b86fc8b1fc6855e81f491a36fc07c4df1bf41cdae483a0e4e988bf79148ac2d4517e66f435c3072353bd2fec1a407d90306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a40000000069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f0000000000106ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90d0751a8282da61305fe299c37b998e58471db1135037310f8be1045a60af6ee4157b0580f31c5fce44a62582dbcf9d78ee75943a084a393b350368d228993084bd949c43602c33f207790ed16a3524ca1b9975cf121a2a90cffec7df8b68acd6ebb742bdee2c768b9c43f8b90eda90d68f365f01d5989a2755375b1c750fb21b78bdce2aabc5f99b530a91d01df3af8f6d8910a78ba10d61c75c87b9e558472c8a88f9f203e4707e698c8e61c3d610cdb67947cc60b50819eee8e0cb2d3f15d174b15a58411b7ff1d320c6e232580e5940f24b38ea10580b38eefaa462193ba030f00090310270000000000000f000502c0270900161600101703070b140213050e0d01120a0c09060408151128f8c69e91e17587c80027b92900000000292c4dd9f52e03004096f92a000000005a00000000000000", + "meta": "0a0f0a0d0800000002190000001e00000010f8551a5ce8d1b2930789b985c2aa0280eaf50280fa65f0bb7cc0d2910bc0bcbbe606a1e0d0ee18f0bb7cc0bcfbd901c099db01f0bb7cc0bcfbd901f0bb7c80ace0070189b4f6e0bd0c8098b4bd03c0d545cbc895f005c0d54500c0d545e09a59225cf0fbb1930789b985c2aa0280eaf50280fa65f0bb7cc0d2910bc0bcbbe606a1e0d0ee18f0bb7cc0bcfbd901c099db01f0bb7cc0bcfbd901f0bb7c80ace0070189b4f6e0bd0c8098b4bd03c0d545cbc895f005c0d54500c0d545e09a592a2f0802122b08141212110213050e0d01120a0c0906040815070b001a11090027b92900000000292c4dd9f52e03002002323e50726f6772616d20436f6d7075746542756467657431313131313131313131313131313131313131313131313131313131313120696e766f6b65205b315d323b50726f6772616d20436f6d707574654275646765743131313131313131313131313131313131313131313131313131313131312073756363657373323e50726f6772616d20436f6d7075746542756467657431313131313131313131313131313131313131313131313131313131313120696e766f6b65205b315d323b50726f6772616d20436f6d707574654275646765743131313131313131313131313131313131313131313131313131313131312073756363657373323f50726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c5220696e766f6b65205b315d321e50726f6772616d206c6f673a20496e737472756374696f6e3a2053776170323f50726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d703820696e766f6b65205b325d326250726f6772616d206c6f673a207261795f6c6f673a204177416e75536b41414141414b53784e32665575417741424141414141414141414445537459304241414141413041684479504d4d41475a50694b6f4567414141465473594130506f674941323250726f6772616d206c6f673a204572726f723a2065786365656473206465736972656420736c697070616765206c696d6974325b50726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d703820636f6e73756d6564203136333136206f662035343330383020636f6d7075746520756e697473325750726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d7038206661696c65643a20637573746f6d2070726f6772616d206572726f723a2030783165325b50726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c5220636f6e73756d6564203732393336206f662035393937303020636f6d7075746520756e697473325750726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c52206661696c65643a20637573746f6d2070726f6772616d206572726f723a20307831653ab2010801122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2609fc4c41a75508544010091a0b3830313330323237383635220c38302e313330323237383635222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a95010804122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413ab0010807122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2409d404f63d8fb01a4010091a0a36363732343231343235220b362e363732343231343235222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a94010808122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a9501080b122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413abf01080d122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a32096fac7a2f6174944110091a113835373932383433383639373938343033221238353739323834332e383639373938343033222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142b2010801122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2609fc4c41a75508544010091a0b3830313330323237383635220c38302e313330323237383635222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544414295010804122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142b0010807122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2409d404f63d8fb01a4010091a0a36363732343231343235220b362e363732343231343235222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544414294010808122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b5046584357754276663953733632335651354441429501080b122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142bf01080d122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a32096fac7a2f6174944110091a113835373932383433383639373938343033221238353739323834332e383639373938343033222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544417801800194bc04", + "index": 0 + }], + "rewards": "0a400a2c3550487370394a5a74583533317a6435353652796b696b6e6f55615271477a6e366154693752537550656d4b10b6ef9be90218eea190a10b20042a0231300a400a2c4533796850733550504e34525a683846624a6f32657174647241594b434b394837706353443176434e435034109ec981f70618d6e8ed9d1620042a023130" + } +}] diff --git a/old-faithful-proto/tests/decode_confirmed_transaction.json b/old-faithful-proto/tests/decode_confirmed_transaction.json new file mode 100644 index 00000000..6dba5763 --- /dev/null +++ b/old-faithful-proto/tests/decode_confirmed_transaction.json @@ -0,0 +1,13 @@ +[{ + "name": "versioned+protobuf", + "value": { + "transaction": { + "transaction": "0199eebba8126354ea9913f40d3d5e5e0f027b3fa91fc794cdcb5b52978faff3adb2a6133cb985d062c7efeb10504cdcfcea02056254a4e898021343be1624ae0901000918d15f7fe461963ba26a3d4f595fcb1b75319c71714e7adafd05eb050db8b9168e0f9902b27c4dcbe1886026ea28b0f62a8e2a6f2fc2d6dc6039977d2659dc7b9b1711b21c8c13b92ead6d5a2443be061626f011d369c5137e94311325955b5a5918e9c4e91dadd620e64ad7fb58e681f582ac49c288bda541b7fa05de7e20592d1aeea271624534f0b40324926b14166ac85f435f996bff7b9ee3daff9ad70b7f3c5dcabdbb2d21b72d8538a3e0cf0ddcf709c98549d6e096dffe77e5d0a718893c97b7d1275147489f796be09686250eef836bda271815faf49b6486428f6edd4ac9fb73dceb9b7fa7d0c17bd5bcf4993db73eafd9f6d40f1c009998629aa5e960682dc41274e0a2bec3e6f855579ae0119159d342463c714532039f6270e5b56a2907a91a6addd1c4137cf5050558bba02a9d2746a6923b7b661d553c31d67281f73ef0e76451d812ca8b4842937674e1ee408557e0d975dd8e4e53fe432b87b0d93e3c5b35446308000a666c598f90c607314a665dc195296535d75af1664ada4f06fab5fbcde8dee7921baa861c07c24e47ea148a64e4328c9ea74ca68a83eb956f0b680bfbe5f65ba8bb0060ff311b86fc8b1fc6855e81f491a36fc07c4df1bf41cdae483a0e4e988bf79148ac2d4517e66f435c3072353bd2fec1a407d90306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a40000000069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f0000000000106ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90d0751a8282da61305fe299c37b998e58471db1135037310f8be1045a60af6ee4157b0580f31c5fce44a62582dbcf9d78ee75943a084a393b350368d228993084bd949c43602c33f207790ed16a3524ca1b9975cf121a2a90cffec7df8b68acd6ebb742bdee2c768b9c43f8b90eda90d68f365f01d5989a2755375b1c750fb21b78bdce2aabc5f99b530a91d01df3af8f6d8910a78ba10d61c75c87b9e558472c8a88f9f203e4707e698c8e61c3d610cdb67947cc60b50819eee8e0cb2d3f15d174b15a58411b7ff1d320c6e232580e5940f24b38ea10580b38eefaa462193ba030f00090310270000000000000f000502c0270900161600101703070b140213050e0d01120a0c09060408151128f8c69e91e17587c80027b92900000000292c4dd9f52e03004096f92a000000005a00000000000000", + "meta": "0a0f0a0d0800000002190000001e00000010f8551a5ce8d1b2930789b985c2aa0280eaf50280fa65f0bb7cc0d2910bc0bcbbe606a1e0d0ee18f0bb7cc0bcfbd901c099db01f0bb7cc0bcfbd901f0bb7c80ace0070189b4f6e0bd0c8098b4bd03c0d545cbc895f005c0d54500c0d545e09a59225cf0fbb1930789b985c2aa0280eaf50280fa65f0bb7cc0d2910bc0bcbbe606a1e0d0ee18f0bb7cc0bcfbd901c099db01f0bb7cc0bcfbd901f0bb7c80ace0070189b4f6e0bd0c8098b4bd03c0d545cbc895f005c0d54500c0d545e09a592a2f0802122b08141212110213050e0d01120a0c0906040815070b001a11090027b92900000000292c4dd9f52e03002002323e50726f6772616d20436f6d7075746542756467657431313131313131313131313131313131313131313131313131313131313120696e766f6b65205b315d323b50726f6772616d20436f6d707574654275646765743131313131313131313131313131313131313131313131313131313131312073756363657373323e50726f6772616d20436f6d7075746542756467657431313131313131313131313131313131313131313131313131313131313120696e766f6b65205b315d323b50726f6772616d20436f6d707574654275646765743131313131313131313131313131313131313131313131313131313131312073756363657373323f50726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c5220696e766f6b65205b315d321e50726f6772616d206c6f673a20496e737472756374696f6e3a2053776170323f50726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d703820696e766f6b65205b325d326250726f6772616d206c6f673a207261795f6c6f673a204177416e75536b41414141414b53784e32665575417741424141414141414141414445537459304241414141413041684479504d4d41475a50694b6f4567414141465473594130506f674941323250726f6772616d206c6f673a204572726f723a2065786365656473206465736972656420736c697070616765206c696d6974325b50726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d703820636f6e73756d6564203136333136206f662035343330383020636f6d7075746520756e697473325750726f6772616d203637356b5058394d48546a53327a7431716672314e5948757a654c5866514d394832347746535574314d7038206661696c65643a20637573746f6d2070726f6772616d206572726f723a2030783165325b50726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c5220636f6e73756d6564203732393336206f662035393937303020636f6d7075746520756e697473325750726f6772616d20444d564a6478566754326932726d6f4b383263515a544637574872795543526f7158396e6e546f5653614c52206661696c65643a20637573746f6d2070726f6772616d206572726f723a20307831653ab2010801122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2609fc4c41a75508544010091a0b3830313330323237383635220c38302e313330323237383635222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a95010804122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413ab0010807122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2409d404f63d8fb01a4010091a0a36363732343231343235220b362e363732343231343235222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a94010808122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413a9501080b122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544413abf01080d122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a32096fac7a2f6174944110091a113835373932383433383639373938343033221238353739323834332e383639373938343033222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142b2010801122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2609fc4c41a75508544010091a0b3830313330323237383635220c38302e313330323237383635222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544414295010804122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142b0010807122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a2409d404f63d8fb01a4010091a0a36363732343231343235220b362e363732343231343235222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544414294010808122b536f31313131313131313131313131313131313131313131313131313131313131313131313131313131321a0810091a0130220130222c385477533870424137706577567258654c72735165565365734733737a6d6863317464375154526d747575312a2b546f6b656e6b65675166655a79694e77414a624e62474b5046584357754276663953733632335651354441429501080b122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a0810091a0130220130222c46364a666f7a7674346a31627456563762373974736a363745723348315938594469774d7a45766d706473732a2b546f6b656e6b65675166655a79694e77414a624e62474b504658435775427666395373363233565135444142bf01080d122c4557486369547a34583751617374436d4e46694533574d586d474e586146507143793347534b535475356f321a32096fac7a2f6174944110091a113835373932383433383639373938343033221238353739323834332e383639373938343033222c3551353434664b72466f65367473456244375338456d7847544a59414b745456684157355135706765346a312a2b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544417801800194bc04", + "index": 0 + }, + "slot": 42, + "block_time": 42, + "index": 0 + } +}]