diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 3d4502f..6eec4c6 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -24,12 +24,10 @@ parser_error_t rs_compute_address(keys_t *keys, uint32_t account, uint8_t *rando // use to compute the full-viewing key parser_error_t rs_compute_keys(keys_t *keys); -// use to compute the full-viewing key -parser_error_t rs_compute_effect_hash(); - parser_error_t rs_compute_transaction_plan(transaction_plan_t *plan, uint8_t *output, size_t output_len); parser_error_t rs_spend_action_hash(spend_key_bytes_t *sk, spend_plan_t *plan, uint8_t *output, size_t output_len); +parser_error_t rs_output_action_hash(spend_key_bytes_t *sk, output_plan_t *plan, bytes_t *memo_key, uint8_t *output, size_t output_len); int32_t rs_bech32_encode(const uint8_t *hrp_ptr, size_t hrp_len, const uint8_t *data_ptr, size_t data_len, uint8_t *output_ptr, size_t output_len); diff --git a/app/rust/src/parser.rs b/app/rust/src/parser.rs index 44ba859..62e5d25 100644 --- a/app/rust/src/parser.rs +++ b/app/rust/src/parser.rs @@ -31,8 +31,6 @@ mod precision; mod tx_parameters; mod bytes; -pub use address::Address; -pub use clue_plan::CluePlan; pub use curve_fields::{Fq, Fr}; pub use error::ParserError; pub use object_list::ObjectList; diff --git a/app/rust/src/parser/address.rs b/app/rust/src/parser/address.rs index a81038e..b650942 100644 --- a/app/rust/src/parser/address.rs +++ b/app/rust/src/parser/address.rs @@ -40,43 +40,6 @@ use nom::bytes::complete::take; use super::bytes::BytesC; use crate::{utils::varint, FromBytes, ParserError}; -// TODO! It is unclear if this address is a raw-address to which F4Jumble and bech32 encoding -// has been applied as well, if that is the case, its length is not necessarly ADDRESS_LEN -// it could be more due to bech32 hrp and checksum. - -#[cfg_attr(test, derive(Debug))] -#[derive(Copy, PartialEq, Eq, Clone)] -pub struct Address<'a>(&'a [u8]); - -impl<'b> FromBytes<'b> for Address<'b> { - fn from_bytes_into( - input: &'b [u8], - out: &mut MaybeUninit, - ) -> Result<&'b [u8], nom::Err> { - let out = out.as_mut_ptr(); - - let (input, _) = varint(input)?; // Parse field number and wire type - let (input, len) = varint(input)?; // Parse length - // - if len as usize == 0 { - return Err(ParserError::InvalidLength.into()); - } - - // TODO: not necessarly equal but less than - // if len as usize != ADDRESS_LEN { - // return Err(ParserError::InvalidLength.into()); - // } - - let (input, bytes) = take(len as usize)(input)?; - - unsafe { - addr_of_mut!((*out).0).write(bytes); - } - - Ok(input) - } -} - #[repr(C)] #[derive(Clone, PartialEq, Default)] #[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] diff --git a/app/rust/src/parser/clue_plan.rs b/app/rust/src/parser/clue_plan.rs index c8783ee..4df390f 100644 --- a/app/rust/src/parser/clue_plan.rs +++ b/app/rust/src/parser/clue_plan.rs @@ -13,50 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ -use core::ptr::addr_of_mut; - -use crate::{ - utils::{read_fixed_bytes, varint}, - FromBytes, -}; - use crate::parser::{address::AddressC, bytes::BytesC}; -use super::{Address, Precision}; - -#[cfg_attr(test, derive(Debug))] -#[derive(Copy, Clone)] -pub struct CluePlan<'a> { - pub address: Address<'a>, - pub rseed: &'a [u8; 32], - pub precision: Precision, -} - -impl<'a> FromBytes<'a> for CluePlan<'a> { - fn from_bytes_into( - input: &'a [u8], - out: &mut core::mem::MaybeUninit, - ) -> Result<&'a [u8], nom::Err> { - let out = out.as_mut_ptr(); - - let addr = unsafe { &mut *addr_of_mut!((*out).address).cast() }; - let rem = Address::from_bytes_into(input, addr)?; - - let (rem, rseed) = read_fixed_bytes::<32>(rem)?; - - let (rem, precision) = varint(rem)?; - - let precision = Precision::try_from(precision)?; - - unsafe { - addr_of_mut!((*out).rseed).write(rseed); - addr_of_mut!((*out).precision).write(precision); - } - - Ok(rem) - } -} - #[repr(C)] #[derive(Clone, Default)] #[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] diff --git a/app/rust/src/parser/note.rs b/app/rust/src/parser/note.rs index 70ff1be..aaca3a7 100644 --- a/app/rust/src/parser/note.rs +++ b/app/rust/src/parser/note.rs @@ -2,10 +2,11 @@ use crate::address::Address; use crate::parser::{ address::AddressC, bytes::BytesC, - plans::{rseed::Rseed, value::{Value, ValueC}}, + plans::{rseed::Rseed, value::{Value, ValueC}, symmetric::OvkWrappedKey}, }; + use crate::ParserError; -use decaf377::Fq; +use decaf377::{Fq, Fr}; pub struct Note { /// The typed value recorded by this note. @@ -50,6 +51,16 @@ impl TryFrom for Note { } impl Note { + pub fn from_parts(address: Address, value: Value, rseed: Rseed) -> Result { + Ok(Note { + value, + rseed, + address: address.clone(), + transmission_key_s: Fq::from_bytes_checked(&address.transmission_key().0) + .map_err(|_| ParserError::InvalidFvk)?, + }) + } + pub fn commit(&self) -> Result { let commit = poseidon377::hash_6( &Self::notecommit_domain_sep(), @@ -65,6 +76,24 @@ impl Note { Ok(commit) } + // /// Generate encrypted outgoing cipher key for use with this note. + // pub fn encrypt_key(&self, ovk: &OutgoingViewingKey, cv: balance::Commitment) -> OvkWrappedKey { + // let esk = self.ephemeral_secret_key(); + // let epk = esk.diversified_public(&self.diversified_generator()); + // let ock = OutgoingCipherKey::derive(ovk, cv, self.commit(), &epk); + // let shared_secret = esk + // .key_agreement_with(self.transmission_key()) + // .expect("key agreement succeeded"); + + // let encryption_result = ock.encrypt(shared_secret.0.to_vec(), PayloadKind::Note); + + // OvkWrappedKey( + // encryption_result + // .try_into() + // .expect("OVK encryption result fits in ciphertext len"), + // ) + // } + pub fn note_blinding(&self) -> Result { let rseed = self.rseed.derive_note_blinding()?; Ok(rseed) @@ -83,4 +112,8 @@ impl Note { pub fn notecommit_domain_sep() -> Fq { Fq::from_le_bytes_mod_order(blake2b_simd::blake2b(b"penumbra.notecommit").as_bytes()) } + + pub fn ephemeral_secret_key(&self) -> Result { + self.rseed.derive_esk() + } } diff --git a/app/rust/src/parser/plans.rs b/app/rust/src/parser/plans.rs index 310b013..59534e8 100644 --- a/app/rust/src/parser/plans.rs +++ b/app/rust/src/parser/plans.rs @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ -use self::{detection::DetectionDataPlanC, memo::MemoPlanC, spend::SpendPlanC, action::ActionsHashC}; +use self::{detection::DetectionDataPlanC, memo::MemoPlanC, spend::SpendPlanC, action::ActionsHashC, output::OutputPlanC}; use crate::keys::spend_key::SpendKeyBytes; use crate::effect_hash::EffectHash; - +use crate::parser::bytes::BytesC; pub mod action; pub mod amount; pub mod balance; @@ -30,6 +30,7 @@ pub mod rseed; pub mod spend; pub mod symmetric; pub mod value; +pub mod output; use super::tx_parameters::TransactionParametersC; use crate::constants::ACTION_DATA_QTY; @@ -66,15 +67,6 @@ impl TransactionPlanC { } } -#[no_mangle] -/// Use to compute an address and write it back into output -/// argument. -pub unsafe extern "C" fn rs_compute_effect_hash() -> u32 { - crate::zlog("rs_compute_effect_hash\x00"); - - ParserError::Ok as u32 -} - #[no_mangle] /// Use to compute an address and write it back into output /// argument. @@ -128,6 +120,40 @@ pub unsafe extern "C" fn rs_spend_action_hash( ParserError::Ok as u32 } +#[no_mangle] +/// Use to compute an address and write it back into output +/// argument. +pub unsafe extern "C" fn rs_output_action_hash( + sk: &SpendKeyBytes, + plan: &OutputPlanC, + memo_key: &BytesC, + output: *mut u8, + output_len: usize, +) -> u32 { + crate::zlog("rs_spend_action_hash\x00"); + let output = std::slice::from_raw_parts_mut(output, output_len); + + if output.len() < 64 { + return ParserError::Ok as u32; + } + + let fvk: crate::keys::FullViewingKey = sk.fvk().unwrap(); + let memo_key_bytes = match memo_key.get_bytes() { + Ok(bytes) => bytes, + Err(_) => &[0u8; 32], + }; + + let body_hash_bytes = plan.effect_hash(&fvk, &memo_key_bytes); + + if let Ok(body_hash_bytes) = body_hash_bytes { + let body_hash_array = body_hash_bytes.as_array(); + let copy_len: usize = core::cmp::min(output.len(), body_hash_array.len()); + output[..copy_len].copy_from_slice(&body_hash_array[..copy_len]); + } + + ParserError::Ok as u32 +} + #[cfg(test)] mod tests { use super::*; @@ -312,4 +338,70 @@ mod tests { panic!("spend_action_hash is not Ok"); } } + + #[test] + fn test_output_action_hash() { + // Create dummy ActionC + let dummy_amount = AmountC { + lo: 535446340456032950, + hi: 0, + }; + + let asset_id_bytes = + hex::decode("29ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a10") + .unwrap(); + let dummy_asset_id = IdC { + inner: BytesC::from_slice(&asset_id_bytes), + }; + + let dummy_value = ValueC { + amount: dummy_amount, + asset_id: dummy_asset_id, + }; + + let dummy_address_inner = hex::decode("f72c37238af64e9c8517e4cac09a43a99cee8aa4cb7e2c20419f55dd06f0884bfbfa5202b88852edda3d54273de22c4ef40edb4bc54c0c14fd0b5475d33433d0bd9793c8670795eb822b94c3cbb1a412").unwrap(); + let dummy_address = AddressC { + inner: BytesC::from_slice(&dummy_address_inner), + alt_bech32m: BytesC::default(), + }; + + let dummy_rseed_bytes = + hex::decode("28fc41cb8153082b110af95a0eb013a25c4248bdc25ab2f7c7e0041258d01c42") + .unwrap(); + + let dummy_value_blinding_bytes = + hex::decode("4c19474a9edb1933a643ae2b2648131061b95b25fb6ffeafb3e53ccacf8fe700") + .unwrap(); + let dummy_proof_blinding_r_bytes = + hex::decode("825b816bfb539eb34a7933f362ab7b9a3fe128074a1603a5c43afb125d44e002") + .unwrap(); + let dummy_proof_blinding_s_bytes = + hex::decode("86ae5038cfd758ee6520792a143ea401ef8e2afbc70f65c0b6e1d58b3492b211") + .unwrap(); + + let dummy_action = OutputPlanC { + value: dummy_value, + dest_address: dummy_address, + rseed: BytesC::from_slice(&dummy_rseed_bytes), + value_blinding: BytesC::from_slice(&dummy_value_blinding_bytes), + proof_blinding_r: BytesC::from_slice(&dummy_proof_blinding_r_bytes), + proof_blinding_s: BytesC::from_slice(&dummy_proof_blinding_s_bytes), + }; + + let spend_key = SpendKeyBytes::from([ + 0xa1, 0xff, 0xba, 0x0c, 0x37, 0x93, 0x1f, 0x0a, 0x62, 0x61, 0x37, 0x52, 0x0d, 0xa6, + 0x50, 0x63, 0x2d, 0x35, 0x85, 0x3b, 0xf5, 0x91, 0xb3, 0x6b, 0xb4, 0x28, 0x63, 0x0a, + 0x4d, 0x87, 0xc4, 0xdc, + ]); + let fvk = spend_key.fvk().unwrap(); + + let output_action_hash = dummy_action.effect_hash(&fvk, &[0u8; 32]); + let expected_hash = "27e17cfb588ad724519232789f2cfdb724b3072ac2db6e411b3504eb8e05ab1d26545495dc9e1b8a59ad049706102b2c58f23aba6a346f8ff4b595ce4e1d8f4c"; + if let Ok(output_action_hash_bytes) = output_action_hash { + let computed_hash = hex::encode(output_action_hash_bytes.as_array()); + assert_eq!(computed_hash, expected_hash); + } else { + panic!("output_action_hash is not Ok"); + } + } } diff --git a/app/rust/src/parser/plans/detection.rs b/app/rust/src/parser/plans/detection.rs index e51261e..1ac00af 100644 --- a/app/rust/src/parser/plans/detection.rs +++ b/app/rust/src/parser/plans/detection.rs @@ -17,42 +17,8 @@ use crate::address::Address; use crate::constants::DETECTION_DATA_QTY; use crate::effect_hash::{create_personalized_state, EffectHash}; -use crate::keys::clue_key::Clue; use crate::parser::clue_plan::CluePlanC; use crate::ParserError; -use core::{mem::MaybeUninit, ptr::addr_of_mut}; - -use crate::{ - parser::{CluePlan, ObjectList}, - utils::varint, - FromBytes, -}; - -#[cfg_attr(test, derive(Debug))] -#[derive(Copy, PartialEq, Eq, Clone)] -pub struct DetectionDataPlan<'a>(ObjectList<'a, CluePlan<'a>>); - -impl<'a> FromBytes<'a> for DetectionDataPlan<'a> { - fn from_bytes_into( - input: &'a [u8], - out: &mut core::mem::MaybeUninit, - ) -> Result<&'a [u8], nom::Err> { - let out = out.as_mut_ptr(); - - let (rem, size) = varint(input)?; - - let detection: &mut MaybeUninit>> = - unsafe { &mut *addr_of_mut!((*out).0).cast() }; - - let rem = ObjectList::new_into_with_len(rem, detection, size as usize)?; - - Ok(rem) - } -} - -pub struct DetectionData { - pub fmd_clues: [Clue; DETECTION_DATA_QTY], -} #[repr(C)] #[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] diff --git a/app/rust/src/parser/plans/memo.rs b/app/rust/src/parser/plans/memo.rs index 3b8cdbf..e389950 100644 --- a/app/rust/src/parser/plans/memo.rs +++ b/app/rust/src/parser/plans/memo.rs @@ -45,6 +45,10 @@ impl MemoPlanC { && self.plaintext.text.len == 0 && self.key.len == 0 } + + pub fn get_memo_key(&self) -> Result<&[u8], ParserError> { + self.key.get_bytes() + } } pub struct MemoCiphertext(pub [u8; MEMO_CIPHERTEXT_LEN_BYTES]); diff --git a/app/rust/src/parser/plans/output.rs b/app/rust/src/parser/plans/output.rs new file mode 100644 index 0000000..4a657c0 --- /dev/null +++ b/app/rust/src/parser/plans/output.rs @@ -0,0 +1,91 @@ +use crate::effect_hash::{create_personalized_state, EffectHash}; +use crate::keys::FullViewingKey; +use crate::parser::{ + bytes::BytesC, + address::AddressC, + plans::{nullifier::Nullifier, value::{Value, ValueC, Sign}, rseed::Rseed}, +}; +use crate::address::Address; +use crate::parser::note::Note; +use crate::parser::plans::symmetric::PayloadKey; +use crate::ParserError; +use decaf377::Fr; + +pub struct Body { + pub note_payload: [u8; 32], + pub balance_commitment: [u8; 32], + pub ovk_wrapped_key: [u8; 32], + pub wrapped_memo_key: [u8; 32], +} + +#[repr(C)] +#[derive(Clone)] +#[cfg_attr(any(feature = "derive-debug", test), derive(Debug))] +pub struct OutputPlanC { + pub value: ValueC, + pub dest_address: AddressC, + pub rseed: BytesC, + pub value_blinding: BytesC, + pub proof_blinding_r: BytesC, + pub proof_blinding_s: BytesC, +} + +impl OutputPlanC { + pub fn effect_hash(&self, fvk: &FullViewingKey, memo_key: &[u8]) -> Result { + let memo_payload_key = PayloadKey::from_bytes(memo_key); + let body = self.body(fvk, &memo_payload_key)?; + + + Err(ParserError::InvalidLength) + + } + + pub fn body(&self, fvk: &FullViewingKey, memo_key: &PayloadKey) -> Result { + + let ovk = fvk.outgoing(); + let note = self.output_note()?; + let value = self.balance()?; + let balance_commitment = value.commit(self.get_value_blinding_fr()?, Sign::Required)?; + + // Encrypt the note to the recipient... + let esk = note.ephemeral_secret_key()?; + + // ... and wrap the encryption key to ourselves. + //let ovk_wrapped_key = note.encrypt_key(ovk, balance_commitment); + + + Ok(Body { + note_payload: [0u8; 32], + balance_commitment: [0u8; 32], + ovk_wrapped_key: [0u8; 32], + wrapped_memo_key: [0u8; 32], + }) + } + + pub fn output_note(&self) -> Result { + let value = Value::try_from(self.value.clone())?; + let rseed = Rseed::try_from(self.rseed.clone())?; + let address = Address::try_from(self.dest_address.inner.get_bytes()?)?; + + Note::from_parts(address, value, rseed) + } + + pub fn balance(&self) -> Result { + // We should return a Balance struct here, but since we are currently managing only one value, it isn’t necessary for now + let value = Value::try_from(self.value.clone())?; + Ok(value) + } + + pub fn get_rseed(&self) -> Result<&[u8], ParserError> { + self.rseed.get_bytes() + } + + pub fn get_value_blinding(&self) -> Result<&[u8], ParserError> { + self.value_blinding.get_bytes() + } + + pub fn get_value_blinding_fr(&self) -> Result { + let value_blinding_bytes = self.get_value_blinding()?; + Ok(Fr::from_le_bytes_mod_order(value_blinding_bytes)) + } +} diff --git a/app/rust/src/parser/plans/rseed.rs b/app/rust/src/parser/plans/rseed.rs index 67aaaff..db1991b 100644 --- a/app/rust/src/parser/plans/rseed.rs +++ b/app/rust/src/parser/plans/rseed.rs @@ -1,10 +1,17 @@ -use decaf377::Fq; +use decaf377::{Fq, Fr}; use crate::{ParserError, utils::prf}; use crate::parser::bytes::BytesC; pub struct Rseed(pub [u8; 32]); impl Rseed { + /// Derive the ephemeral secret key from the rseed. + pub fn derive_esk(&self) -> Result { + let hash_result = prf::expand(b"Penumbra_DeriEsk", &self.0, &[4u8])?; + println!("derive_esk - HASH_RESULT: {:?}", hash_result); + Ok(Fr::from_le_bytes_mod_order(&hash_result)) + } + /// Derive note commitment randomness from the rseed. pub fn derive_note_blinding(&self) -> Result { let hash_result = prf::expand(b"Penumbra_DeriRcm", &self.0, &[5u8])?; diff --git a/app/rust/src/parser/plans/spend.rs b/app/rust/src/parser/plans/spend.rs index 2cd0fda..690ba6d 100644 --- a/app/rust/src/parser/plans/spend.rs +++ b/app/rust/src/parser/plans/spend.rs @@ -3,72 +3,10 @@ use crate::keys::FullViewingKey; use crate::parser::{ bytes::BytesC, note::{Note, NoteC}, - plans::{nullifier::Nullifier, value::Value}, -}; + plans::{nullifier::Nullifier, value::{Value, Sign}}, +}; use crate::ParserError; -use decaf377::{Fq, Fr}; - -// proto: -// message SpendPlan { -// // The plaintext note we plan to spend. -// Note note = 1; -// // The position of the note we plan to spend. -// uint64 position = 2; -// // The randomizer to use for the spend. -// bytes randomizer = 3; -// // The blinding factor to use for the value commitment. -// bytes value_blinding = 4; -// // The first blinding factor to use for the ZK spend proof. -// bytes proof_blinding_r = 5; -// // The second blinding factor to use for the ZK spend proof. -// bytes proof_blinding_s = 6; -// } -#[cfg_attr(test, derive(Debug))] -#[derive(Copy, PartialEq, Eq, Clone)] -// pub struct SpendPlan<'a> { -// pub note: Note<'a>, -// pub position: Position, -// pub randomizer: Fr<'a>, -// pub value_blinding: Fr<'a>, -// pub proof_blinding_r: Fq<'a>, -// pub proof_blinding_s: Fq<'a>, -// } - -// impl<'b> FromBytes<'b> for SpendPlan<'b> { -// fn from_bytes_into( -// input: &'b [u8], -// out: &mut MaybeUninit, -// ) -> Result<&'b [u8], nom::Err> { -// let output = out.as_mut_ptr(); - -// // Parsing each field sequentially -// // Parse `note` -// let note = unsafe { &mut *addr_of_mut!((*output).note).cast() }; -// let input = Note::from_bytes_into(input, note)?; - -// // Parse `position` -// let position = unsafe { &mut *addr_of_mut!((*output).position).cast() }; -// let input = Position::from_bytes_into(input, position)?; - -// // Parse `randomizer` -// let randomizer = unsafe { &mut *addr_of_mut!((*output).randomizer).cast() }; -// let input = Fr::from_bytes_into(input, randomizer)?; - -// // Parse `value_blinding` -// let value = unsafe { &mut *addr_of_mut!((*output).value_blinding).cast() }; -// let input = Fr::from_bytes_into(input, value)?; - -// // Parse `proof_blinding_r` -// let proof = unsafe { &mut *addr_of_mut!((*output).proof_blinding_r).cast() }; -// let input = Fq::from_bytes_into(input, proof)?; - -// // Parse `proof_blinding_s` -// let proof = unsafe { &mut *addr_of_mut!((*output).proof_blinding_s).cast() }; -// let input = Fq::from_bytes_into(input, proof)?; - -// Ok(input) -// } -// } +use decaf377::Fr; pub struct Body { pub balance_commitment: [u8; 32], @@ -117,7 +55,7 @@ impl SpendPlanC { pub fn body(&self, fvk: &FullViewingKey) -> Result { let value = self.balance()?; - let balance_commitment = value.commit(self.get_value_blinding_fr()?)?; + let balance_commitment = value.commit(self.get_value_blinding_fr()?, Sign::Provided)?; Ok(Body { balance_commitment, diff --git a/app/rust/src/parser/plans/symmetric.rs b/app/rust/src/parser/plans/symmetric.rs index 78a9892..0e6dc72 100644 --- a/app/rust/src/parser/plans/symmetric.rs +++ b/app/rust/src/parser/plans/symmetric.rs @@ -86,3 +86,7 @@ impl PayloadKey { } } + +/// Represents encrypted key material used to reconstruct a `PayloadKey`. +#[derive(Clone, Debug)] +pub struct OvkWrappedKey(pub [u8; OVK_WRAPPED_LEN_BYTES]); diff --git a/app/rust/src/parser/plans/value.rs b/app/rust/src/parser/plans/value.rs index c7254d5..714f7ac 100644 --- a/app/rust/src/parser/plans/value.rs +++ b/app/rust/src/parser/plans/value.rs @@ -6,6 +6,12 @@ use crate::parser::{ }; use decaf377::Fr; +// this should be in imbalance.rs. For now, it’s not necessary +pub enum Sign { + Required, + Provided, +} + #[derive(Clone, Debug)] pub struct Value { pub amount: Amount, @@ -15,11 +21,19 @@ pub struct Value { impl Value { // this should be implemented in the Balance, but since we are currently managing only one value, it isn’t necessary for now - pub fn commit(&self, blinding_factor: Fr) -> Result<[u8; 32], ParserError> { + pub fn commit(&self, blinding_factor: Fr, sign: Sign) -> Result<[u8; 32], ParserError> { let mut commitment = decaf377::Element::IDENTITY; let g_v = self.asset_id.value_generator(); let amount_fr: Fr = Into::into(self.amount.clone()); - commitment += g_v * amount_fr; + + match sign { + Sign::Required => { + commitment -= g_v * amount_fr; + } + Sign::Provided => { + commitment += g_v * amount_fr; + } + } let value_blinding_generator = Commitment::value_blinding_generator(); commitment += blinding_factor * value_blinding_generator; diff --git a/app/src/crypto_helper.c b/app/src/crypto_helper.c index 423d18f..58abb38 100644 --- a/app/src/crypto_helper.c +++ b/app/src/crypto_helper.c @@ -41,10 +41,3 @@ zxerr_t compute_keys(keys_t *keys) { return zxerr_ok; } -zxerr_t compute_effect_hash() { - if (rs_compute_effect_hash() != parser_ok) { - return zxerr_unknown; - } - - return zxerr_ok; -} diff --git a/app/src/crypto_helper.h b/app/src/crypto_helper.h index 1950f04..1e2ad6a 100644 --- a/app/src/crypto_helper.h +++ b/app/src/crypto_helper.h @@ -47,7 +47,6 @@ extern "C" { zxerr_t compute_address(keys_t *keys, uint32_t account, uint8_t *randomizer); zxerr_t compute_keys(keys_t *keys); -zxerr_t compute_effect_hash(); #ifdef __cplusplus } diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 0c434e9..d6cf504 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -285,6 +285,16 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { // printf("amount hi: %lu\n", actions_plan[i].action.spend.note.value.amount.hi); // printf("amount lo: %lu\n", actions_plan[i].action.spend.note.value.amount.lo); break; + case penumbra_core_transaction_v1_ActionPlan_output_tag: + print_buffer(&actions_plan[i].action.output.value.asset_id.inner, "real output action note value asset id inner"); + printf("output value amount hi: %lu\n", actions_plan[i].action.output.value.amount.hi); + printf("output value amount lo: %lu\n", actions_plan[i].action.output.value.amount.lo); + print_buffer(&actions_plan[i].action.output.dest_address.inner, "real output action note dest address inner"); + print_buffer(&actions_plan[i].action.output.rseed, "real output action note rseed"); + print_buffer(&actions_plan[i].action.output.value_blinding, "real output action note value_blinding"); + print_buffer(&actions_plan[i].action.output.proof_blinding_r, "real output action note proof_blinding_r"); + print_buffer(&actions_plan[i].action.output.proof_blinding_s, "real output action note proof_blinding_s"); + break; } } @@ -295,8 +305,13 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { print_buffer(&v->plan.memo.plaintext.return_address.alt_bech32m, "real memo return address alt bech32m"); 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, &v->plan.actions.hashes[i]); + switch (actions_plan[i].action_type) { + case penumbra_core_transaction_v1_ActionPlan_spend_tag: + compute_spend_action_hash(&actions_plan[i].action.spend, &v->plan.actions.hashes[i]); + break; + case penumbra_core_transaction_v1_ActionPlan_output_tag: + compute_output_action_hash(&actions_plan[i].action.output, &v->plan.actions.hashes[i]); + break; } } v->plan.actions.qty = actions_qty; diff --git a/app/src/parser_interface.c b/app/src/parser_interface.c index edbce21..5c64c42 100644 --- a/app/src/parser_interface.c +++ b/app/src/parser_interface.c @@ -69,3 +69,24 @@ parser_error_t compute_spend_action_hash(spend_plan_t *plan, action_hash_t *outp return parser_ok; } + +parser_error_t compute_output_action_hash(output_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 = { + 0xa1, 0xff, 0xba, 0x0c, 0x37, 0x93, 0x1f, 0x0a, 0x62, 0x61, 0x37, 0x52, 0x0d, 0xa6, 0x50, 0x63, + 0x2d, 0x35, 0x85, 0x3b, 0xf5, 0x91, 0xb3, 0x6b, 0xb4, 0x28, 0x63, 0x0a, 0x4d, 0x87, 0xc4, 0xdc + }; + Bytes_t memo; + + if (rs_output_action_hash(&sk_bytes, plan, &memo, (uint8_t *)output, 64) != parser_ok) { + return parser_unexpected_error; + } + + // TODO: only for testing + print_buffer_interface((uint8_t *)output, 64, "output action hash"); + + return parser_ok; +} \ No newline at end of file diff --git a/app/src/parser_interface.h b/app/src/parser_interface.h index 583bd36..47a715a 100644 --- a/app/src/parser_interface.h +++ b/app/src/parser_interface.h @@ -30,6 +30,7 @@ extern "C" { parser_error_t compute_transaction_plan(transaction_plan_t *plan); parser_error_t compute_spend_action_hash(spend_plan_t *plan, action_hash_t *output); +parser_error_t compute_output_action_hash(output_plan_t *plan, action_hash_t *output); #ifdef __cplusplus } diff --git a/tests/parser_impl.cpp b/tests/parser_impl.cpp index 282f062..8d442b1 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), - "0abe020abb020aa8010a300a0a08f6fec6bbb0eda1e10a12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a1012202263a493b8201d37bc24d2bb459212cd9e3d88c6afb707d43283a0e80d3839591a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10ca9bb6be92d60a1a204950faad59ed83dbbbf19633dabcb3c82259961ebf297bf194948af065c820042220d69fe89087a1ae87386ac5ecf89f1170ade0317b2de9958e6566fe5cc47a5f002a2060d3f34832aaaf0f6de5f974171c261597ba66b0903a85fb823436498e2dba0a32201282a0d9c64554db3f2518da1598fd9c92bb33677e8855f730dc13b75b633a0d0abd020aba020aa8010a300a0a08ecd69fe7c6e9e2ab0b12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a101220e6d9e25c36380ffb8263f57bc5c93893f75a26167a03e2ac029f62e0fed2fb021a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10fbaaaebabc741a208966acb303c3dbdb7f480d39d207d72495563e00a083ad8cce351faaaf2aeb022220bbe68d3e70458b4b5ffd860c374fb40930b07a639d4c7c7077928d9ac0e868012a208f01c1ebcd5582b05da53bd8ea147a6257b0160966c4b9237f144694adc72702322017be7f31a2da7e50001f277754757d9f5ab9ed3c0a27dc2218dee79d4d6815120abe020abb020aa8010a300a0a08fcd0d382feba9cff0212220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a101220aff1713cd7fa8c9eac9db187dd4ee116e3af522e914b91317e8ae962f79902f31a520a50890bc98e3698aa4578e419b028da5672e627c280d8b06166f4c42d5366bccf1fcf3b296cd61e8d744a21f75f2fb697183e18595d8a79008539d8fb138b405db09db65cc42d54c0e772e5d42d5f20b52f10fcfe94f3b0fe171a2019f0b16d495c974d1d7ad0e80257d69997f8c6e45a1e63b5c419c61da3a48b042220975392c6113a406257578b0cc5c5086e8100b1b7328f1445a924a83b0f1876012a20c99bc5e70a76b5fd612f01e031f9b5df38d523e0c148eb52c72b037882ae010b32202e8ce2aa8c19a80895bd55f64eaf6eae6b1fb783096f4c42b6b844bfcdaaf7051229089e8eed98081213776b636b616e2d3432373935373034323035371a0c0a0a08e383f09ac580a0a906"); + "0a9102128e020a300a0a08b6d5a8b9c1b392b70712220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a1012520a50f72c37238af64e9c8517e4cac09a43a99cee8aa4cb7e2c20419f55dd06f0884bfbfa5202b88852edda3d54273de22c4ef40edb4bc54c0c14fd0b5475d33433d0bd9793c8670795eb822b94c3cbb1a4121a2028fc41cb8153082b110af95a0eb013a25c4248bdc25ab2f7c7e0041258d01c4222204c19474a9edb1933a643ae2b2648131061b95b25fb6ffeafb3e53ccacf8fe7002a20825b816bfb539eb34a7933f362ab7b9a3fe128074a1603a5c43afb125d44e002322086ae5038cfd758ee6520792a143ea401ef8e2afbc70f65c0b6e1d58b3492b211123408e999a48e19121e6777706a6b77706f796d64772d30383537303637313838303430383730341a0c0a0a08bcccd9c7dccb878d01"); parser_parse(&ctx, buffer, bufferLen, &tx_obj); } diff --git a/tests/parser_test.txt b/tests/parser_test.txt deleted file mode 100644 index 5b5e319..0000000 --- a/tests/parser_test.txt +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* -* (c) 2018 - 2023 Zondax AG -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -// #{TODO} --> Apply tests that check this app's encoding/libraries - -#include "gmock/gmock.h" - -#include -#include -#include -#include "parser_txdef.h" -#include "parser.h" -#include "parser_impl.h" -#include "keys_def.h" -#include "crypto_helper.h" -#include "zxformat.h" - -using namespace std; - -TEST(SCALE, skvTest) { - const std::string spending_key = "5614d258049853afbf1ca97bec7ccf8e64e0d90a3d417ef0f3c6f7431685e5c1"; - const std::string expected_fvk = "3cd58bbb8725bfe4566504b04d7a31b67bb67fd5d09a28364ac7ac2c2fd8710e4fb0d8c51486fc24938ca96564842a84201d266c92b72761e4e99a16b3405103"; - - keys_t keys; - parseHexString(keys.skb, KEY_LEN, spending_key.c_str()); - compute_effect_hash(); - - char hexstr[200]; - array_to_hexstr(hexstr, 200, keys.fvk, sizeof(keys.fvk)); - std::string str(hexstr); - - EXPECT_EQ(str, expected_fvk); -}