From 68095642a194ada815198e9d3e4cc1362098f5fc Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Fri, 27 Jan 2023 17:08:58 +0200 Subject: [PATCH] Recombined Tx and SigningTx. --- .../lib/node/ledger/shell/process_proposal.rs | 3 +- core/src/proto/mod.rs | 3 +- core/src/proto/types.rs | 182 +++++++++--------- core/src/types/transaction/encrypted.rs | 2 - core/src/types/transaction/mod.rs | 12 +- proto/types.proto | 1 + shared/src/ledger/protocol/mod.rs | 4 +- tests/src/vm_host_env/mod.rs | 57 +++--- tests/src/vm_host_env/tx.rs | 2 +- 9 files changed, 129 insertions(+), 137 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 598498e005..86b932bd37 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -223,6 +223,7 @@ mod test_process_proposal { use namada::types::token::Amount; use namada::types::transaction::encrypted::EncryptedTx; use namada::types::transaction::{EncryptionKey, Fee, WrapperTx}; + use namada::proto::TxCode; use super::*; use crate::facade::tendermint_proto::abci::RequestInitChain; @@ -329,7 +330,7 @@ mod test_process_proposal { .try_to_vec() .expect("Test failed"); Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some( SignedTxData { sig, diff --git a/core/src/proto/mod.rs b/core/src/proto/mod.rs index 5d5cc70379..6a37e99bb1 100644 --- a/core/src/proto/mod.rs +++ b/core/src/proto/mod.rs @@ -3,7 +3,7 @@ pub mod generated; mod types; -pub use types::{Dkg, Error, Signed, SignedTxData, Tx}; +pub use types::{Dkg, Error, Signed, SignedTxData, Tx, TxCode}; #[cfg(test)] mod tests { @@ -17,6 +17,7 @@ mod tests { fn encoding_round_trip() { let tx = Tx { code: "wasm code".as_bytes().to_owned(), + is_literal: true, data: Some("arbitrary data".as_bytes().to_owned()), timestamp: Some(std::time::SystemTime::now().into()), inner_tx: None, diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index d87d904b65..cf197195ff 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -128,97 +128,48 @@ where } } -/// A Tx with its code replaced by a hash salted with the Borsh -/// serialized timestamp of the transaction. This structure will almost -/// certainly be smaller than a Tx, yet in the usual cases it contains -/// enough information to confirm that the Tx is as intended and make a -/// non-malleable signature. +/// Represents either the literal code of a transaction or its hash. #[derive( - Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema, Hash, + Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Hash, PartialEq, Eq, )] -pub struct SigningTx { - pub code_hash: [u8; 32], - pub data: Option>, - pub timestamp: DateTimeUtc, +pub enum TxCode { + Hash([u8; 32]), + Literal(Vec), } -impl SigningTx { - pub fn hash(&self) -> [u8; 32] { - let timestamp = Some(self.timestamp.into()); - let mut bytes = vec![]; - types::Tx { - code: self.code_hash.to_vec(), - data: self.data.clone(), - timestamp, - inner_tx: None, - inner_tx_code: None, +impl TxCode { + pub fn code(&self) -> Option> { + match self { + Self::Hash(_hash) => None, + Self::Literal(lit) => Some(lit.clone()), } - .encode(&mut bytes) - .expect("encoding a transaction failed"); - hash_tx(&bytes).0 } - - /// Sign a transaction using [`SignedTxData`]. - pub fn sign(self, keypair: &common::SecretKey) -> Self { - let to_sign = self.hash(); - let sig = common::SigScheme::sign(keypair, to_sign); - let signed = SignedTxData { - data: self.data, - sig, - } - .try_to_vec() - .expect("Encoding transaction data shouldn't fail"); - SigningTx { - code_hash: self.code_hash, - data: Some(signed), - timestamp: self.timestamp, + pub fn code_hash(&self) -> [u8; 32] { + match self { + Self::Hash(hash) => hash.clone(), + Self::Literal(lit) => hash_tx(lit).0, } } - - /// Verify that the transaction has been signed by the secret key - /// counterpart of the given public key. - pub fn verify_sig( - &self, - pk: &common::PublicKey, - sig: &common::Signature, - ) -> std::result::Result<(), VerifySigError> { - // Try to get the transaction data from decoded `SignedTxData` - let tx_data = self.data.clone().ok_or(VerifySigError::MissingData)?; - let signed_tx_data = SignedTxData::try_from_slice(&tx_data[..]) - .expect("Decoding transaction data shouldn't fail"); - let data = signed_tx_data.data; - let tx = SigningTx { - code_hash: self.code_hash, - data, - timestamp: self.timestamp, - }; - let signed_data = tx.hash(); - common::SigScheme::verify_signature_raw(pk, &signed_data, sig) - } - /// Expand this reduced Tx using the supplied code only if the the code /// hashes to the stored code hash - pub fn expand(self, code: Vec) -> Option { - if hash_tx(&code).0 == self.code_hash { - Some(Tx { - code, - data: self.data, - timestamp: self.timestamp, - inner_tx: None, - inner_tx_code: None, - }) - } else { - None + pub fn expand(&mut self, code: Vec) { + if TxCode::Hash(hash_tx(&code).0) == *self { + *self = TxCode::Literal(code); } } -} - -impl From for SigningTx { - fn from(tx: Tx) -> SigningTx { - SigningTx { - code_hash: hash_tx(&tx.code).0, - data: tx.data, - timestamp: tx.timestamp, + pub fn contract(&mut self) { + *self = TxCode::Hash(self.code_hash()); + } + pub fn is_literal(&self) -> bool { + match self { + Self::Literal(_) => true, + _ => false, + } + } + pub fn is_hash(&self) -> bool { + match self { + Self::Hash(_) => true, + _ => false, } } } @@ -230,7 +181,7 @@ impl From for SigningTx { Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, )] pub struct Tx { - pub code: Vec, + pub code: TxCode, pub data: Option>, pub timestamp: DateTimeUtc, /// the encrypted payload @@ -284,8 +235,13 @@ impl TryFrom<&[u8]> for Tx { ), None => None, }; + let code = if tx.is_literal { + TxCode::Literal(tx.code) + } else { + TxCode::Hash(tx.code.try_into().expect("Unable to deserialize code hash")) + }; Ok(Tx { - code: tx.code, + code, data: tx.data, timestamp, inner_tx, @@ -309,7 +265,8 @@ impl From for types::Tx { }; types::Tx { - code: tx.code, + code: tx.code.code().unwrap_or(tx.code.code_hash().to_vec()), + is_literal: tx.code.is_literal(), data: tx.data, timestamp, inner_tx, @@ -407,7 +364,7 @@ impl From for ResponseDeliverTx { impl Tx { pub fn new(code: Vec, data: Option>) -> Self { Tx { - code, + code: TxCode::Literal(code), data, timestamp: DateTimeUtc::now(), inner_tx: None, @@ -424,23 +381,42 @@ impl Tx { } pub fn hash(&self) -> [u8; 32] { - SigningTx::from(self.clone()).hash() + let timestamp = Some(self.timestamp.into()); + let mut bytes = vec![]; + types::Tx { + code: self.code.code().unwrap_or(self.code.code_hash().to_vec()), + is_literal: self.code.is_literal(), + data: self.data.clone(), + timestamp, + inner_tx: None, + inner_tx_code: None, + } + .encode(&mut bytes) + .expect("encoding a transaction failed"); + hash_tx(&bytes).0 } pub fn code_hash(&self) -> [u8; 32] { - SigningTx::from(self.clone()).code_hash + self.code.code_hash() } /// Sign a transaction using [`SignedTxData`]. - pub fn sign(mut self, keypair: &common::SecretKey) -> Self { - let code = self.code.clone(); - let inner_tx = std::mem::take(&mut self.inner_tx); - let inner_tx_code = std::mem::take(&mut self.inner_tx_code); - let tx = SigningTx::from(self) - .sign(keypair) - .expand(code) - .expect("code hashes to unexpected value"); - Self { inner_tx, inner_tx_code, ..tx } + pub fn sign(self, keypair: &common::SecretKey) -> Self { + let to_sign = self.hash(); + let sig = common::SigScheme::sign(keypair, to_sign); + let signed = SignedTxData { + data: self.data, + sig, + } + .try_to_vec() + .expect("Encoding transaction data shouldn't fail"); + Tx { + code: self.code, + data: Some(signed), + timestamp: self.timestamp, + inner_tx: self.inner_tx, + inner_tx_code: self.inner_tx_code, + } } /// Verify that the transaction has been signed by the secret key @@ -450,7 +426,20 @@ impl Tx { pk: &common::PublicKey, sig: &common::Signature, ) -> std::result::Result<(), VerifySigError> { - SigningTx::from(self.clone()).verify_sig(pk, sig) + // Try to get the transaction data from decoded `SignedTxData` + let tx_data = self.data.clone().ok_or(VerifySigError::MissingData)?; + let signed_tx_data = SignedTxData::try_from_slice(&tx_data[..]) + .expect("Decoding transaction data shouldn't fail"); + let data = signed_tx_data.data; + let tx = Tx { + code: self.code.clone(), + data, + timestamp: self.timestamp, + inner_tx: self.inner_tx.clone(), + inner_tx_code: self.inner_tx_code.clone(), + }; + let signed_data = tx.hash(); + common::SigScheme::verify_signature_raw(pk, &signed_data, sig) } #[cfg(feature = "ferveo-tpke")] @@ -555,7 +544,8 @@ mod tests { assert_eq!(tx_from_bytes, tx); let types_tx = types::Tx { - code, + code: code.clone(), + is_literal: true, data: Some(data), timestamp: None, inner_tx: None, diff --git a/core/src/types/transaction/encrypted.rs b/core/src/types/transaction/encrypted.rs index 8e3a77bb4c..b73868bbba 100644 --- a/core/src/types/transaction/encrypted.rs +++ b/core/src/types/transaction/encrypted.rs @@ -4,8 +4,6 @@ #[cfg(feature = "ferveo-tpke")] pub mod encrypted_tx { use std::io::{Error, ErrorKind, Write}; - use std::hash::Hasher; - use std::hash::Hash; use ark_ec::PairingEngine; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index d3ae8cb5e7..3d1333a4d6 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -293,19 +293,19 @@ pub mod tx_types { .map(|data| SignedTxData::try_from_slice(&data[..])) { let signed_hash = Tx { - code: tx.code, + code: tx.code.clone(), data: Some(data.clone()), timestamp: tx.timestamp, - inner_tx: None, - inner_tx_code: None, + inner_tx: tx.inner_tx.clone(), + inner_tx_code: tx.inner_tx_code.clone(), } .hash(); match TxType::try_from(Tx { - code: vec![], + code: tx.code, data: Some(data), timestamp: tx.timestamp, - inner_tx: None, - inner_tx_code: None, + inner_tx: tx.inner_tx, + inner_tx_code: tx.inner_tx_code, }) .map_err(|err| TxError::Deserialization(err.to_string()))? { diff --git a/proto/types.proto b/proto/types.proto index 287fc4c34f..49c2d53442 100644 --- a/proto/types.proto +++ b/proto/types.proto @@ -11,6 +11,7 @@ message Tx { google.protobuf.Timestamp timestamp = 3; optional bytes inner_tx = 4; optional bytes inner_tx_code = 5; + bool is_literal = 6; } message Dkg { string data = 1; } diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index cfd416c14d..05ff742b1e 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -162,7 +162,7 @@ where CA: 'static + WasmCacheAccess + Sync, { gas_meter - .add_compiling_fee(tx.code.len()) + .add_compiling_fee(tx.code.code().expect("tx code not present").len()) .map_err(Error::GasError)?; let empty = vec![]; let tx_data = tx.data.as_ref().unwrap_or(&empty); @@ -171,7 +171,7 @@ where write_log, gas_meter, tx_index, - &tx.code, + &tx.code.code().expect("tx code not present"), tx_data, vp_wasm_cache, tx_wasm_cache, diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index f9b5367b4c..88775902de 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -45,6 +45,7 @@ mod tests { use super::{ibc, tx, vp}; use crate::tx::{tx_host_env, TestTxEnv}; use crate::vp::{vp_host_env, TestVpEnv}; + use crate::namada::proto::TxCode; // paths to the WASMs used for tests const VP_ALWAYS_TRUE_WASM: &str = "../wasm_for_tests/vp_always_true.wasm"; @@ -537,7 +538,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -576,7 +577,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -611,7 +612,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -658,7 +659,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -689,7 +690,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -729,7 +730,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -768,7 +769,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -796,7 +797,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -834,7 +835,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -863,7 +864,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -906,7 +907,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -949,7 +950,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -995,7 +996,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1021,7 +1022,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1061,7 +1062,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1088,7 +1089,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1130,7 +1131,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1172,7 +1173,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1219,7 +1220,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1261,7 +1262,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1312,7 +1313,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1379,7 +1380,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1458,7 +1459,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1507,7 +1508,7 @@ mod tests { .encode(&mut tx_data) .expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1538,7 +1539,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1591,7 +1592,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1655,7 +1656,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, @@ -1729,7 +1730,7 @@ mod tests { let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); let tx = Tx { - code: vec![], + code: TxCode::Literal(vec![]), data: Some(tx_data.clone()), timestamp: DateTimeUtc::now(), inner_tx: None, diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index 0f7040941d..a8452fee5d 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -191,7 +191,7 @@ impl TestTxEnv { &mut self.write_log, &mut self.gas_meter, &self.tx_index, - &self.tx.code, + &self.tx.code.code().expect("tx code not present"), self.tx.data.as_ref().unwrap_or(&empty_data), &mut self.vp_wasm_cache, &mut self.tx_wasm_cache,