diff --git a/app/rust/src/parser/bytes.rs b/app/rust/src/parser/bytes.rs index 1c130245..57ea9a09 100644 --- a/app/rust/src/parser/bytes.rs +++ b/app/rust/src/parser/bytes.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ +use crate::ParserError; #[cfg(any(feature = "derive-debug", test))] use core::fmt; @@ -35,11 +36,11 @@ impl fmt::Debug for BytesC { } impl BytesC { - pub fn get_bytes(&self) -> Option<&[u8]> { + pub fn get_bytes(&self) -> Result<&[u8], ParserError> { if self.ptr.is_null() || self.len == 0 { - None + Err(ParserError::UnexpectedData) } else { - unsafe { Some(std::slice::from_raw_parts(self.ptr, self.len as usize)) } + unsafe { Ok(std::slice::from_raw_parts(self.ptr, self.len as usize)) } } } } diff --git a/app/rust/src/parser/note.rs b/app/rust/src/parser/note.rs index a5ad03f7..944d1d39 100644 --- a/app/rust/src/parser/note.rs +++ b/app/rust/src/parser/note.rs @@ -75,22 +75,15 @@ impl NoteC { } pub fn get_rseed(&self) -> Result<&[u8], ParserError> { - self.rseed.get_bytes().ok_or(ParserError::UnexpectedData) + self.rseed.get_bytes() } pub fn get_asset_id(&self) -> Result<&[u8], ParserError> { - self.value - .asset_id - .inner - .get_bytes() - .ok_or(ParserError::UnexpectedData) + self.value.asset_id.inner.get_bytes() } pub fn get_address(&self) -> Result<&[u8], ParserError> { - self.address - .inner - .get_bytes() - .ok_or(ParserError::UnexpectedData) + self.address.inner.get_bytes() } pub fn get_asset_id_fq(&self) -> Result { diff --git a/app/rust/src/parser/plans.rs b/app/rust/src/parser/plans.rs index 278b261d..310b013c 100644 --- a/app/rust/src/parser/plans.rs +++ b/app/rust/src/parser/plans.rs @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ -use self::{detection::DetectionDataPlanC, memo::MemoPlanC, spend::SpendPlanC}; +use self::{detection::DetectionDataPlanC, memo::MemoPlanC, spend::SpendPlanC, action::ActionsHashC}; use crate::keys::spend_key::SpendKeyBytes; -use crate::parser::bytes::BytesC; +use crate::effect_hash::EffectHash; pub mod action; pub mod amount; @@ -38,12 +38,34 @@ use crate::ParserError; #[repr(C)] #[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] pub struct TransactionPlanC { - pub actions_hashes: [BytesC; ACTION_DATA_QTY], + pub actions_hashes: ActionsHashC, pub transaction_parameters: TransactionParametersC, pub memo: MemoPlanC, pub detection_data: DetectionDataPlanC, } +impl TransactionPlanC { + pub fn effect_hash(&self) -> Result { + let mut state = blake2b_simd::Params::new() + .personal(b"PenumbraEfHs") + .to_state(); + + state.update(self.transaction_parameters.effect_hash()?.as_array()); + state.update(self.memo.effect_hash()?.as_array()); + state.update(self.detection_data.effect_hash()?.as_array()); + + let num_actions = self.actions_hashes.qty as u32; + state.update(&num_actions.to_le_bytes()); + + for i in 0..num_actions { + let action_hash = self.actions_hashes.hashes[i as usize].0; + state.update(&action_hash); + } + + Ok(EffectHash::from_array(*state.finalize().as_array())) + } +} + #[no_mangle] /// Use to compute an address and write it back into output /// argument. @@ -65,27 +87,14 @@ pub unsafe extern "C" fn rs_compute_transaction_plan( let output = std::slice::from_raw_parts_mut(output, output_len); if output.len() < 200 { - return ParserError::Ok as u32; - } - - let transaction_parameters_hash = plan.transaction_parameters.effect_hash(); - if let Ok(transaction_parameters_hash_bytes) = transaction_parameters_hash { - let transaction_parameters_hash_array = transaction_parameters_hash_bytes.as_array(); - let copy_len: usize = core::cmp::min(output.len(), transaction_parameters_hash_array.len()); - output[..copy_len].copy_from_slice(&transaction_parameters_hash_array[..copy_len]); + return ParserError::UnexpectedData as u32; } - if let Ok(memo_hash_bytes) = plan.memo.effect_hash() { - let memo_hash_array = memo_hash_bytes.as_array(); - let copy_len: usize = core::cmp::min(output.len() - 64, memo_hash_array.len()); - output[68..68 + copy_len].copy_from_slice(&memo_hash_array[..copy_len]); - } - - let detection_hash = plan.detection_data.effect_hash(); - if let Ok(detection_hash_bytes) = detection_hash { - let detection_hash_array = detection_hash_bytes.as_array(); - let copy_len: usize = core::cmp::min(output.len() - 136, detection_hash_array.len()); - output[136..136 + copy_len].copy_from_slice(&detection_hash_array[..copy_len]); + let plan_hash_result = plan.effect_hash(); + if let Ok(plan_hash) = plan_hash_result { + let plan_hash_array = plan_hash.as_array(); + let copy_len: usize = core::cmp::min(output.len(), plan_hash_array.len()); + output[..copy_len].copy_from_slice(&plan_hash_array[..copy_len]); } ParserError::Ok as u32 @@ -127,7 +136,7 @@ mod tests { use crate::parser::bytes::BytesC; use crate::parser::clue_plan::CluePlanC; use crate::parser::note::NoteC; - use crate::parser::plans::action::ActionC; + use crate::parser::plans::action::ActionsHashC; use crate::parser::plans::amount::AmountC; use crate::parser::plans::detection::DetectionDataPlanC; use crate::parser::plans::id::IdC; @@ -135,12 +144,12 @@ mod tests { use crate::parser::plans::memo_plain_text::MemoPlaintextC; use crate::parser::plans::value::ValueC; use crate::parser::tx_parameters::TransactionParametersC; + use crate::parser::plans::action::ActionHash; #[test] fn test_transaction_plan_hash() { - // Create dummy ActionC - let dummy_action = ActionC { - action_type: 0, // Assuming 0 is a valid action type - bytes: BytesC::from_slice(&[0u8; 32]), + let dummy_action_hashes = ActionsHashC { + qty: 1, + hashes: core::array::from_fn(|_| ActionHash([0u8; 64])), }; // Create dummy TransactionParametersC @@ -198,7 +207,7 @@ mod tests { // Create TransactionPlanC with dummy data let transaction_plan = TransactionPlanC { - actions_hashes: core::array::from_fn(|_| BytesC::from_slice(&[0u8; 32])), + actions_hashes: dummy_action_hashes, transaction_parameters: dummy_transaction_parameters, memo: dummy_memo_plan, detection_data: dummy_detection_data, diff --git a/app/rust/src/parser/plans/action.rs b/app/rust/src/parser/plans/action.rs index 2ae94047..94a55a2b 100644 --- a/app/rust/src/parser/plans/action.rs +++ b/app/rust/src/parser/plans/action.rs @@ -1,5 +1,3 @@ -use core::ptr::addr_of_mut; - /******************************************************************************* * (c) 2024 Zondax GmbH * @@ -15,10 +13,10 @@ use core::ptr::addr_of_mut; * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ -use crate::{FromBytes, ParserError}; - // use super::spend::SpendPlan; use crate::parser::bytes::BytesC; +use crate::constants::ACTION_DATA_QTY; + #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -26,68 +24,16 @@ pub enum ActionType { Spend = 0, } -impl TryFrom for ActionType { - type Error = ParserError; - fn try_from(value: u64) -> Result { - match value { - 0 => Ok(Self::Spend), - _ => Err(ParserError::InvalidActionType), - } - } -} - -// #[repr(C)] -// struct SpendVariant<'a>(ActionType, SpendPlan<'a>); - -// #[cfg_attr(test, derive(Debug))] -// #[derive(Copy, PartialEq, Eq, Clone)] -// pub enum ActionPlan<'a> { -// Spend(SpendPlan<'a>), -// } - -// impl<'a> FromBytes<'a> for ActionPlan<'a> { -// fn from_bytes_into( -// input: &'a [u8], -// out: &mut core::mem::MaybeUninit, -// ) -> Result<&'a [u8], nom::Err> { -// // 1. Read the action plan type -// let (rem, action_type) = (input, 0); // TODO! read from input - -// match action_type { -// 0 => { -// // Spend variant -// let out = out.as_mut_ptr() as *mut SpendVariant<'a>; -// let data = unsafe { &mut *addr_of_mut!((*out).1).cast() }; -// let rem = SpendPlan::from_bytes_into(rem, data)?; -// unsafe { -// addr_of_mut!((*out).0).write(ActionType::Spend); -// } -// Ok(rem) -// } -// _ => Err(ParserError::InvalidActionType.into()), -// } -// } -// } - -// impl<'a> ActionPlan<'a> { -// pub fn action(&self) -> ActionType { -// match self { -// Self::Spend(_) => ActionType::Spend, -// } -// } - -// pub fn spend_plan(&self) -> Option<&SpendPlan<'a>> { -// match self { -// Self::Spend(info) => Some(info), -// } -// } -// } +#[repr(C)] +#[derive(Copy, Clone)] +#[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] +pub struct ActionHash(pub [u8; 64]); #[repr(C)] #[derive(Clone)] #[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] -pub struct ActionC { - pub action_type: u8, - pub bytes: BytesC, +pub struct ActionsHashC { + pub qty: u8, + pub hashes: [ActionHash; ACTION_DATA_QTY], } diff --git a/app/rust/src/parser/plans/id.rs b/app/rust/src/parser/plans/id.rs index 0e7b6dd2..8f7acd0f 100644 --- a/app/rust/src/parser/plans/id.rs +++ b/app/rust/src/parser/plans/id.rs @@ -32,7 +32,7 @@ pub struct IdC { } impl IdC { pub fn get_inner(&self) -> Result<&[u8], ParserError> { - self.inner.get_bytes().ok_or(ParserError::UnexpectedData) + self.inner.get_bytes() } } diff --git a/app/rust/src/parser/plans/memo.rs b/app/rust/src/parser/plans/memo.rs index 6660eea1..3b8cdbf3 100644 --- a/app/rust/src/parser/plans/memo.rs +++ b/app/rust/src/parser/plans/memo.rs @@ -67,7 +67,7 @@ impl MemoCiphertext { ciphertext[..memo_bytes.len()].copy_from_slice(memo_bytes); // Get memo key bytes - let memo_key_bytes = memo_key.get_bytes().ok_or(ParserError::UnexpectedError)?; + let memo_key_bytes = memo_key.get_bytes()?; // Create PayloadKey and encrypt let key = PayloadKey::from_bytes(memo_key_bytes); diff --git a/app/rust/src/parser/plans/spend.rs b/app/rust/src/parser/plans/spend.rs index 5adbaf0c..85538632 100644 --- a/app/rust/src/parser/plans/spend.rs +++ b/app/rust/src/parser/plans/spend.rs @@ -148,9 +148,7 @@ impl SpendPlanC { } pub fn get_randomizer(&self) -> Result<&[u8], ParserError> { - self.randomizer - .get_bytes() - .ok_or(ParserError::UnexpectedData) + self.randomizer.get_bytes() } pub fn get_randomizer_fr(&self) -> Result { @@ -159,9 +157,7 @@ impl SpendPlanC { } pub fn get_value_blinding(&self) -> Result<&[u8], ParserError> { - self.value_blinding - .get_bytes() - .ok_or(ParserError::UnexpectedData) + self.value_blinding.get_bytes() } pub fn get_value_blinding_fr(&self) -> Result { diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 4855375d..0c434e93 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -269,14 +269,6 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { penumbra_core_transaction_v1_TransactionPlan_transaction_parameters_tag); print_buffer(&v->plan.transaction_parameters.parameters, "real transaction parameters"); - // print detection data - for (uint16_t i = 0; i < DETECTION_DATA_QTY; i++) { - // print_buffer(&v->plan.detection_data.clue_plans[i].address.inner, "real detection data address inner"); - // print_buffer(&v->plan.detection_data.clue_plans[i].address.alt_bech32m, "real detection data address alt bech32m"); - // print_buffer(&v->plan.detection_data.clue_plans[i].rseed, "real detection data rseed"); - // printf("precision bits: %lu\n", v->plan.detection_data.clue_plans[i].precision_bits); - } - // print actions for (uint16_t i = 0; i < ACTIONS_QTY; i++) { switch (actions_plan[i].action_type) { @@ -302,11 +294,12 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { print_buffer(&v->plan.memo.plaintext.return_address.inner, "real memo return address inner"); print_buffer(&v->plan.memo.plaintext.return_address.alt_bech32m, "real memo return address alt bech32m"); - for (uint16_t i = 0; i < DETECTION_DATA_QTY; i++) { + for (uint16_t i = 0; i < actions_qty; i++) { if (actions_plan[i].action_type == penumbra_core_transaction_v1_ActionPlan_spend_tag) { - compute_spend_action_hash(&actions_plan[i].action.spend); + compute_spend_action_hash(&actions_plan[i].action.spend, &v->plan.actions.hashes[i]); } } + v->plan.actions.qty = actions_qty; compute_transaction_plan(&v->plan); diff --git a/app/src/parser_interface.c b/app/src/parser_interface.c index bcbc0670..edbce21b 100644 --- a/app/src/parser_interface.c +++ b/app/src/parser_interface.c @@ -21,16 +21,16 @@ #include "rslib.h" #include "zxformat.h" -void print_buffer_interface(Bytes_t *buffer, const char *title) { +void print_buffer_interface(uint8_t *buffer, size_t len, const char *title) { #if defined(LEDGER_SPECIFIC) ZEMU_LOGF(50, "%s\n", title); char print[700] = {0}; - array_to_hexstr(print, sizeof(print), buffer->ptr, buffer->len); + array_to_hexstr(print, sizeof(print), buffer, len); ZEMU_LOGF(700, "%s\n", print); #else printf("%s: ", title); - for (uint16_t i = 0; i < buffer->len; i++) { - printf("%02x", buffer->ptr[i]); + for (size_t i = 0; i < len; i++) { + printf("%02x", buffer[i]); } printf("\n"); #endif @@ -45,16 +45,14 @@ parser_error_t compute_transaction_plan(transaction_plan_t *plan) { } // TODO: only for testing - Bytes_t output_bytes; - output_bytes.ptr = output; - output_bytes.len = 300; - print_buffer_interface(&output_bytes, "output_bytes"); + print_buffer_interface(output, 300, "output_bytes"); return parser_ok; } -parser_error_t compute_spend_action_hash(spend_plan_t *plan) { - if (plan == NULL) return parser_unexpected_error; +parser_error_t compute_spend_action_hash(spend_plan_t *plan, action_hash_t *output) { + if (plan == NULL || output == NULL) + return parser_unexpected_error; // TODO: we need to get the spend key spend_key_bytes_t sk_bytes = { @@ -62,16 +60,12 @@ parser_error_t compute_spend_action_hash(spend_plan_t *plan) { 0x2d, 0x35, 0x85, 0x3b, 0xf5, 0x91, 0xb3, 0x6b, 0xb4, 0x28, 0x63, 0x0a, 0x4d, 0x87, 0xc4, 0xdc }; - uint8_t output[64] = {0}; - if (rs_spend_action_hash(&sk_bytes, plan, output, sizeof(output)) != parser_ok) { + if (rs_spend_action_hash(&sk_bytes, plan, (uint8_t *)output, 64) != parser_ok) { return parser_unexpected_error; } // TODO: only for testing - Bytes_t output_bytes; - output_bytes.ptr = output; - output_bytes.len = 64; - print_buffer_interface(&output_bytes, "spend action hash"); + print_buffer_interface((uint8_t *)output, 64, "spend action hash"); return parser_ok; } diff --git a/app/src/parser_interface.h b/app/src/parser_interface.h index 68734c10..583bd36d 100644 --- a/app/src/parser_interface.h +++ b/app/src/parser_interface.h @@ -29,7 +29,7 @@ extern "C" { #include "zxmacros.h" parser_error_t compute_transaction_plan(transaction_plan_t *plan); -parser_error_t compute_spend_action_hash(spend_plan_t *plan); +parser_error_t compute_spend_action_hash(spend_plan_t *plan, action_hash_t *output); #ifdef __cplusplus } diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index c694fb1d..5400c8af 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -150,12 +150,14 @@ typedef struct { } action; } action_t; +typedef uint8_t action_hash_t[64]; typedef struct { - Bytes_t action_hash; -} action_hash_t; + uint8_t qty; + action_hash_t hashes[ACTIONS_QTY]; +} actions_hash_t; typedef struct { - action_hash_t actions_hash[ACTIONS_QTY]; + actions_hash_t actions; transaction_parameters_t transaction_parameters; memo_plan_t memo; detection_data_t detection_data; diff --git a/tests/parser_impl.cpp b/tests/parser_impl.cpp index d14f8277..282f0628 100644 --- a/tests/parser_impl.cpp +++ b/tests/parser_impl.cpp @@ -36,7 +36,7 @@ TEST(SCALE, ReadBytes) { uint8_t buffer[6000]; auto bufferLen = parseHexString( buffer, sizeof(buffer), - "0abe020abb020aa8010a300a0a08c8daccb4a6f185e40612220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a10122085197c5d60cf28b5ec756a657957b310072396577956fd5cd421ca62b4a6bc091a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10f1a9e496d5f01d1a20732b53ee807140dd5672768ec1a38be09c531a0c6fc185d5f51c18f5f2261d012220f2e2f45f0ea734d7c11321cbf20427b379cfed6f71874ff97e8bcbbfce2d3d012a2073ec22fcaeccfadc720dd0350cf6af7ec274a74be832e8334613638edfd2fb10322093043bfea2094b0398f0e14bccc66a9ec335bbfd1f8e8b4c2c21428947f5e50d121c08cec08d8e1e1206757673762d361a0c0a0a08d6fab2e5c4f992aa0b"); + "0abe020abb020aa8010a300a0a08f6fec6bbb0eda1e10a12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a1012202263a493b8201d37bc24d2bb459212cd9e3d88c6afb707d43283a0e80d3839591a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10ca9bb6be92d60a1a204950faad59ed83dbbbf19633dabcb3c82259961ebf297bf194948af065c820042220d69fe89087a1ae87386ac5ecf89f1170ade0317b2de9958e6566fe5cc47a5f002a2060d3f34832aaaf0f6de5f974171c261597ba66b0903a85fb823436498e2dba0a32201282a0d9c64554db3f2518da1598fd9c92bb33677e8855f730dc13b75b633a0d0abd020aba020aa8010a300a0a08ecd69fe7c6e9e2ab0b12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a101220e6d9e25c36380ffb8263f57bc5c93893f75a26167a03e2ac029f62e0fed2fb021a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10fbaaaebabc741a208966acb303c3dbdb7f480d39d207d72495563e00a083ad8cce351faaaf2aeb022220bbe68d3e70458b4b5ffd860c374fb40930b07a639d4c7c7077928d9ac0e868012a208f01c1ebcd5582b05da53bd8ea147a6257b0160966c4b9237f144694adc72702322017be7f31a2da7e50001f277754757d9f5ab9ed3c0a27dc2218dee79d4d6815120abe020abb020aa8010a300a0a08fcd0d382feba9cff0212220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a101220aff1713cd7fa8c9eac9db187dd4ee116e3af522e914b91317e8ae962f79902f31a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10fcfe94f3b0fe171a2019f0b16d495c974d1d7ad0e80257d69997f8c6e45a1e63b5c419c61da3a48b042220975392c6113a406257578b0cc5c5086e8100b1b7328f1445a924a83b0f1876012a20c99bc5e70a76b5fd612f01e031f9b5df38d523e0c148eb52c72b037882ae010b32202e8ce2aa8c19a80895bd55f64eaf6eae6b1fb783096f4c42b6b844bfcdaaf7051229089e8eed98081213776b636b616e2d3432373935373034323035371a0c0a0a08e383f09ac580a0a906"); parser_parse(&ctx, buffer, bufferLen, &tx_obj); }