diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index a561c1ea46..6a2c4d58d4 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -402,7 +402,7 @@ impl ConsensusConstants { faucet_value: 0.into(), transaction_weight: TransactionWeight::latest(), max_script_byte_size: 512, - max_extra_encrypted_data_byte_size: 256, + max_extra_encrypted_data_byte_size: 240, input_version_range, output_version_range, kernel_version_range, diff --git a/base_layer/core/src/consensus/consensus_encoding/bytes.rs b/base_layer/core/src/consensus/consensus_encoding/bytes.rs index 9a87dcefec..3bd0eff08a 100644 --- a/base_layer/core/src/consensus/consensus_encoding/bytes.rs +++ b/base_layer/core/src/consensus/consensus_encoding/bytes.rs @@ -20,11 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{cmp, convert::TryFrom, ops::Deref}; +use std::{ + cmp, + convert::TryFrom, + ops::{Deref, DerefMut}, +}; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; -use tari_utilities::hex::{from_hex, HexError}; +use tari_utilities::{ + hex::{from_hex, HexError}, + ByteArray, + ByteArrayError, +}; #[derive( Debug, @@ -118,6 +126,27 @@ impl Deref for MaxSizeBytes { } } +impl DerefMut for MaxSizeBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl ByteArray for MaxSizeBytes { + /// Try and convert the given byte array to a MaxSizeBytes. Any failures (incorrect array length, + /// implementation-specific checks, etc) return a [ByteArrayError](enum.ByteArrayError.html). + fn from_canonical_bytes(bytes: &[u8]) -> Result { + Self::from_bytes_checked(bytes).ok_or(ByteArrayError::ConversionError { + reason: "Invalid byte length".to_string(), + }) + } + + /// Return the NodeId as a byte array + fn as_bytes(&self) -> &[u8] { + self.inner.as_ref() + } +} + #[derive(Debug, thiserror::Error)] pub enum MaxSizeBytesError { #[error("Invalid Bytes length: expected {expected}, got {actual}")] diff --git a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs index a3329e9fa2..183ecc0f4b 100644 --- a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs +++ b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs @@ -26,7 +26,7 @@ //! Encrypted data using the extended-nonce variant XChaCha20-Poly1305 encryption with secure random nonce. use std::{ - convert::TryInto, + convert::{TryFrom, TryInto}, fmt, fmt::{Display, Formatter}, mem::size_of, @@ -60,14 +60,14 @@ use thiserror::Error; use zeroize::{Zeroize, Zeroizing}; use super::EncryptedDataKey; -use crate::transactions::tari_amount::MicroMinotari; - +use crate::{consensus::MaxSizeBytes, transactions::tari_amount::MicroMinotari}; // Useful size constants, each in bytes const SIZE_NONCE: usize = size_of::(); const SIZE_VALUE: usize = size_of::(); const SIZE_MASK: usize = PrivateKey::KEY_LEN; const SIZE_TAG: usize = size_of::(); pub const STATIC_ENCRYPTED_DATA_SIZE_TOTAL: usize = SIZE_NONCE + SIZE_VALUE + SIZE_MASK + SIZE_TAG; +const MAX_ENCRYPTED_DATA_SIZE: usize = 256 + STATIC_ENCRYPTED_DATA_SIZE_TOTAL; // Number of hex characters of encrypted data to display on each side of ellipsis when truncating const DISPLAY_CUTOFF: usize = 16; @@ -75,7 +75,7 @@ const DISPLAY_CUTOFF: usize = 16; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Zeroize)] pub struct EncryptedData { #[serde(with = "tari_utilities::serde::hex")] - data: Vec, + data: MaxSizeBytes, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] @@ -185,7 +185,10 @@ impl EncryptedData { data[SIZE_TAG + SIZE_NONCE..SIZE_TAG + SIZE_NONCE + SIZE_VALUE + SIZE_MASK + payment_id.get_size()] .clone_from_slice(bytes.as_slice()); - Ok(Self { data }) + Ok(Self { + data: MaxSizeBytes::try_from(data) + .map_err(|_| EncryptedDataError::IncorrectLength("Data too long".to_string()))?, + }) } /// Authenticate and decrypt the value and mask @@ -235,19 +238,22 @@ impl EncryptedData { bytes.len() ))); } - let mut data = vec![0; bytes.len()]; - data.copy_from_slice(bytes); - Ok(Self { data }) + Ok(Self { + data: MaxSizeBytes::from_bytes_checked(bytes) + .ok_or(EncryptedDataError::IncorrectLength("Data too long".to_string()))?, + }) } #[cfg(test)] pub fn from_vec_unsafe(data: Vec) -> Self { - Self { data } + Self { + data: MaxSizeBytes::from_bytes_checked(data).unwrap(), + } } /// Get a byte vector with the encrypted data contents pub fn to_byte_vec(&self) -> Vec { - self.data.clone() + self.data.clone().into() } /// Get a byte slice with the encrypted data contents @@ -290,11 +296,11 @@ impl Hex for EncryptedData { to_hex(&self.to_byte_vec()) } } - impl Default for EncryptedData { fn default() -> Self { Self { - data: vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL], + data: MaxSizeBytes::try_from(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL]) + .expect("This will always be less then the max length"), } } } diff --git a/base_layer/core/src/validation/block_body/test.rs b/base_layer/core/src/validation/block_body/test.rs index 9e16a1b2b0..68499989dc 100644 --- a/base_layer/core/src/validation/block_body/test.rs +++ b/base_layer/core/src/validation/block_body/test.rs @@ -437,7 +437,7 @@ async fn it_limits_the_encrypted_data_byte_size() { let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await; let mut txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); let mut outputs = txs[0].body.outputs().clone(); - outputs[0].encrypted_data = EncryptedData::from_vec_unsafe(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL + 257]); + outputs[0].encrypted_data = EncryptedData::from_vec_unsafe(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL + 250]); txs[0].body = AggregateBody::new(txs[0].body.inputs().clone(), outputs, txs[0].body.kernels().clone()); let (block, _) = blockchain.create_next_tip(block_spec!("B", transactions: txs)).await;