From 33ae89faeebfd97667f8672d498d1ade4a113844 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Tue, 24 Dec 2024 10:08:00 +0100 Subject: [PATCH] Add description field to test vectors and validate in workflow tests --- zebra-chain/src/orchard_zsa.rs | 2 +- zebra-consensus/src/orchard_zsa/tests.rs | 38 +++--- .../orchard_zsa/tests/block_description.rs | 108 ++++++++++++++++++ zebra-test/src/vectors/orchard_zsa.rs | 7 ++ 4 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 zebra-consensus/src/orchard_zsa/tests/block_description.rs diff --git a/zebra-chain/src/orchard_zsa.rs b/zebra-chain/src/orchard_zsa.rs index d1e55db98b8..8e5f61dbe20 100644 --- a/zebra-chain/src/orchard_zsa.rs +++ b/zebra-chain/src/orchard_zsa.rs @@ -14,7 +14,7 @@ mod burn; mod issuance; pub(crate) use burn::{Burn, NoBurn}; -pub(crate) use issuance::IssueData; +pub use issuance::IssueData; pub use burn::BurnItem; diff --git a/zebra-consensus/src/orchard_zsa/tests.rs b/zebra-consensus/src/orchard_zsa/tests.rs index b85a2856edb..d7e57eec256 100644 --- a/zebra-consensus/src/orchard_zsa/tests.rs +++ b/zebra-consensus/src/orchard_zsa/tests.rs @@ -29,6 +29,10 @@ use zebra_test::{ use crate::{block::Request, Config}; +mod block_description; + +use block_description::build_block_description; + type TranscriptItem = (Request, Result); /// Processes orchard burns, decreasing asset supply. @@ -125,21 +129,27 @@ fn calc_asset_supply_info<'a, I: IntoIterator>( fn create_transcript_data<'a, I: IntoIterator>( serialized_blocks: I, ) -> impl Iterator + use<'a, I> { - let workflow_blocks = - serialized_blocks - .into_iter() - .map(|OrchardZSABlock { bytes, is_valid }| { - ( - Arc::new( - Block::zcash_deserialize(&bytes[..]).expect("block should deserialize"), - ), - *is_valid, - ) - }); - - std::iter::once((regtest_genesis_block(), true)) + let workflow_blocks = serialized_blocks.into_iter().map( + |OrchardZSABlock { + description, + bytes, + is_valid, + }| { + ( + description, + Arc::new(Block::zcash_deserialize(&bytes[..]).expect("block should deserialize")), + *is_valid, + ) + }, + ); + + std::iter::once((&None, regtest_genesis_block(), true)) .chain(workflow_blocks) - .map(|(block, is_valid)| { + .map(|(description, block, is_valid)| { + if let Some(description) = description { + assert_eq!(description, &build_block_description(&block)) + } + ( Request::Commit(block.clone()), if is_valid { diff --git a/zebra-consensus/src/orchard_zsa/tests/block_description.rs b/zebra-consensus/src/orchard_zsa/tests/block_description.rs new file mode 100644 index 00000000000..7f787a2012f --- /dev/null +++ b/zebra-consensus/src/orchard_zsa/tests/block_description.rs @@ -0,0 +1,108 @@ +use orchard::note::AssetBase; + +use zebra_chain::{ + block::Block, + orchard::{OrchardZSA, ShieldedData}, + orchard_zsa::{BurnItem, IssueData}, + transaction::Transaction, +}; + +use hex::encode as encode_hex; + +fn format_asset(asset: &AssetBase) -> String { + encode_hex(asset.to_bytes()).chars().take(6).collect() +} + +fn format_array(strings: impl IntoIterator) -> String { + let text = strings.into_iter().collect::>().join(", "); + + if text.is_empty() { + "[]".to_string() + } else { + format!("[ {text} ]") + } +} + +fn format_transfer(n_actions: usize) -> Option { + (n_actions > 0).then(|| format!("Transfer: {}", n_actions)) +} + +fn format_burn(burn_items: &[BurnItem]) -> Option { + (!burn_items.is_empty()).then(|| { + let burn = burn_items + .iter() + .map(|b| format!("({}, {})", format_asset(&b.asset()), b.raw_amount())); + format!("Burn: {}", format_array(burn)) + }) +} + +fn format_issue(issue_data: &IssueData) -> Option { + let ik = issue_data.inner().ik(); + + let issue = issue_data.actions().map(|action| { + let asset = format_asset(&AssetBase::derive(ik, action.asset_desc())); + let is_finalized = action.is_finalized(); + let notes = action + .notes() + .iter() + .map(|note| note.value().inner().to_string()); + format!("({}, {}, {})", asset, is_finalized, format_array(notes)) + }); + + Some(format!("Issue: {}", format_array(issue))) +} + +fn format_tx_v6( + shielded_data: &Option>, + issue_data: &Option, +) -> String { + let transfer = shielded_data + .as_ref() + .and_then(|shielded_data| format_transfer(shielded_data.actions.len())); + + let burn = shielded_data + .as_ref() + .and_then(|shielded_data| format_burn(shielded_data.burn.as_ref())); + + let issue = issue_data + .as_ref() + .and_then(|issue_data| format_issue(issue_data)); + + format!( + "V6 {{ {} }}", + [transfer, burn, issue] + .into_iter() + .filter_map(|part| part) + .collect::>() + .join(", ") + ) +} + +pub(super) fn build_block_description(block: &Block) -> String { + let height = block + .coinbase_height() + .expect("block has coinbase_height") + .next() + .expect("block has next coinbase_height") + .0; + + let hash = &block.hash().to_string()[..6]; + + let transactions = format_array(block.transactions.iter().map(|tx| match tx.as_ref() { + Transaction::V1 { .. } => "V1".to_string(), + Transaction::V2 { .. } => "V2".to_string(), + Transaction::V3 { .. } => "V3".to_string(), + Transaction::V4 { .. } => "V4".to_string(), + Transaction::V5 { .. } => "V5".to_string(), + Transaction::V6 { + orchard_shielded_data, + orchard_zsa_issue_data, + .. + } => format_tx_v6(orchard_shielded_data, orchard_zsa_issue_data), + })); + + format!( + "Header: ({}, {}), Transactions: {}", + height, hash, transactions + ) +} diff --git a/zebra-test/src/vectors/orchard_zsa.rs b/zebra-test/src/vectors/orchard_zsa.rs index 896b1e0f8fe..b9e92bd9162 100644 --- a/zebra-test/src/vectors/orchard_zsa.rs +++ b/zebra-test/src/vectors/orchard_zsa.rs @@ -7,6 +7,8 @@ use lazy_static::lazy_static; /// Represents a serialized ZSA block and its validity status. pub struct OrchardZSABlock { + /// Description of the cointent of the block + pub description: Option<&'static str>, /// Serialized byte data of the block. pub bytes: Vec, /// Indicates whether the block is valid. @@ -20,22 +22,27 @@ fn decode_bytes(hex: &str) -> Vec { lazy_static! { pub static ref ORCHARD_ZSA_WORKFLOW_BLOCKS: [Vec; 1] = [vec![ OrchardZSABlock { + description: Some("Header: (2, 5fe240), Transactions: [ V4, V6 { Transfer: 2, Issue: [ (444bd8, false, [ 1000 ]) ] } ]"), bytes: decode_bytes(include_str!("orchard-zsa-0-0.txt")), is_valid: true }, OrchardZSABlock { + description: Some("Header: (3, 4450db), Transactions: [ V4, V6 { Transfer: 2 } ]"), bytes: decode_bytes(include_str!("orchard-zsa-0-1.txt")), is_valid: true }, OrchardZSABlock { + description: Some("Header: (4, a1d3c5), Transactions: [ V4, V6 { Transfer: 2, Burn: [ (444bd8, 7) ] }, V6 { Transfer: 2, Burn: [ (444bd8, 2) ] } ]"), bytes: decode_bytes(include_str!("orchard-zsa-0-2.txt")), is_valid: true }, OrchardZSABlock { + description: Some("Header: (5, 854bf0), Transactions: [ V4, V6 { Transfer: 2, Issue: [ (444bd8, true, []) ] } ]"), bytes: decode_bytes(include_str!("orchard-zsa-0-3.txt")), is_valid: true }, OrchardZSABlock { + description: Some("Header: (6, 84f6a1), Transactions: [ V4, V6 { Transfer: 2, Issue: [ (444bd8, false, [ 2000 ]) ] } ]"), bytes: decode_bytes(include_str!("orchard-zsa-0-4.txt")), is_valid: false },