diff --git a/base_layer/core/src/blocks/block.rs b/base_layer/core/src/blocks/block.rs index 0857ef082d..1598632665 100644 --- a/base_layer/core/src/blocks/block.rs +++ b/base_layer/core/src/blocks/block.rs @@ -38,7 +38,7 @@ use thiserror::Error; use crate::{ blocks::BlockHeader, - consensus::{ConsensusConstants, ConsensusDecoding, ConsensusEncoding}, + consensus::{ConsensusConstants, ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}, proof_of_work::ProofOfWork, transactions::{ aggregated_body::AggregateBody, @@ -274,6 +274,12 @@ impl ConsensusEncoding for Block { } } +impl ConsensusEncodingSized for Block { + fn consensus_encode_exact_size(&self) -> usize { + self.header.consensus_encode_exact_size() + self.body.consensus_encode_exact_size() + } +} + impl ConsensusDecoding for Block { fn consensus_decode(reader: &mut R) -> Result { let header = BlockHeader::consensus_decode(reader)?; @@ -325,3 +331,16 @@ impl From<&Block> for NewBlock { } } } + +#[cfg(test)] +mod tests { + use tari_common::configuration::Network; + + use crate::{blocks::genesis_block::get_genesis_block, consensus::check_consensus_encoding_correctness}; + + #[test] + fn block_header_encode_decode() { + let block = get_genesis_block(Network::LocalNet).block().clone(); + check_consensus_encoding_correctness(block).unwrap(); + } +} diff --git a/base_layer/core/src/consensus/consensus_encoding.rs b/base_layer/core/src/consensus/consensus_encoding.rs index 767ae65cfd..2a998daa03 100644 --- a/base_layer/core/src/consensus/consensus_encoding.rs +++ b/base_layer/core/src/consensus/consensus_encoding.rs @@ -71,7 +71,8 @@ impl ToConsensusBytes fo fn to_consensus_bytes(&self) -> Vec { let mut buf = Vec::with_capacity(self.consensus_encode_exact_size()); // Vec's write impl is infallible, as per the ConsensusEncoding contract, consensus_encode is infallible - self.consensus_encode(&mut buf).expect("unreachable panic"); + self.consensus_encode(&mut buf) + .expect("invalid ConsensusEncoding implementation detected"); buf } } @@ -100,8 +101,7 @@ pub mod test { /// ConsensusDecoding implementations pub fn check_consensus_encoding_correctness(subject: T) -> Result<(), io::Error> where T: ConsensusEncoding + ConsensusEncodingSized + ConsensusDecoding + Eq + std::fmt::Debug { - let mut buf = Vec::new(); - subject.consensus_encode(&mut buf)?; + let buf = subject.to_consensus_bytes(); assert_eq!(buf.len(), subject.consensus_encode_exact_size()); let mut reader = buf.as_slice(); let decoded = T::consensus_decode(&mut reader)?; diff --git a/base_layer/core/src/proof_of_work/monero_rx/fixed_array.rs b/base_layer/core/src/proof_of_work/monero_rx/fixed_array.rs index c27495bffe..c1efc1039b 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/fixed_array.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/fixed_array.rs @@ -29,11 +29,11 @@ use std::{ use tari_utilities::{ByteArray, ByteArrayError}; -use crate::consensus::{ConsensusDecoding, ConsensusEncoding}; +use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}; const MAX_ARR_SIZE: usize = 63; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct FixedByteArray { elems: [u8; MAX_ARR_SIZE], len: u8, @@ -134,6 +134,12 @@ impl ConsensusDecoding for FixedByteArray { } } +impl ConsensusEncodingSized for FixedByteArray { + fn consensus_encode_exact_size(&self) -> usize { + self.len as usize + 1 + } +} + impl ConsensusEncoding for FixedByteArray { fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&[self.len][..])?; @@ -147,6 +153,7 @@ mod test { use tari_utilities::hex::Hex; use super::*; + use crate::consensus::check_consensus_encoding_correctness; #[test] fn assert_size() { @@ -173,16 +180,6 @@ mod test { FixedByteArray::from_bytes(&[1u8; 64][..]).unwrap_err(); } - #[test] - fn serialize_deserialize() { - let arr = FixedByteArray::from_hex("ffffffffffffffffffffffffff").unwrap(); - let mut data = Vec::new(); - arr.consensus_encode(&mut data).unwrap(); - assert_eq!(data.len(), 13 + 1); - let arr = FixedByteArray::consensus_decode(&mut data.as_slice()).unwrap(); - assert!(arr.iter().all(|b| *b == 0xff)); - } - #[test] fn length_check() { let mut buf = [0u8; MAX_ARR_SIZE + 1]; @@ -204,4 +201,14 @@ mod test { let data = &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]; let _result = FixedByteArray::consensus_decode(&mut data.as_slice()).unwrap_err(); } + + #[test] + fn consensus_encoding() { + let arr = FixedByteArray::from_hex("ffffffffffffffffffffffffff").unwrap(); + check_consensus_encoding_correctness(arr).unwrap(); + + let arr = FixedByteArray::from_hex("0ff1ceb4d455").unwrap(); + assert_eq!(arr.len(), 6); + check_consensus_encoding_correctness(arr).unwrap(); + } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs index 45787be3bd..194a74aac5 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs @@ -195,7 +195,7 @@ impl ConsensusDecoding for MerkleProof { impl ConsensusEncoding for MerkleProof { fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { - let _ = self.branch.consensus_encode(writer)?; + self.branch.consensus_encode(writer)?; ConsensusEncoding::consensus_encode(&self.depth, writer)?; ConsensusEncoding::consensus_encode(&self.path_bitmap, writer)?; Ok(()) diff --git a/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs b/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs index d6101c017d..73faa7db56 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs @@ -121,12 +121,12 @@ impl ConsensusDecoding for MoneroPowData { impl ConsensusEncoding for MoneroPowData { fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { - let _ = self.header.consensus_encode(writer)?; + self.header.consensus_encode(writer)?; self.randomx_key.consensus_encode(writer)?; ConsensusEncoding::consensus_encode(&self.transaction_count, writer)?; - let _ = self.merkle_root.consensus_encode(writer)?; + self.merkle_root.consensus_encode(writer)?; self.coinbase_merkle_proof.consensus_encode(writer)?; - let _ = self.coinbase_tx.consensus_encode(writer)?; + self.coinbase_tx.consensus_encode(writer)?; Ok(()) } } diff --git a/base_layer/core/src/proof_of_work/proof_of_work.rs b/base_layer/core/src/proof_of_work/proof_of_work.rs index d3378a54b6..0b81a7e823 100644 --- a/base_layer/core/src/proof_of_work/proof_of_work.rs +++ b/base_layer/core/src/proof_of_work/proof_of_work.rs @@ -32,7 +32,7 @@ use serde::{Deserialize, Serialize}; use tari_utilities::hex::Hex; use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, MaxSizeBytes}, + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized, MaxSizeBytes}, proof_of_work::PowAlgorithm, }; @@ -95,6 +95,12 @@ impl ConsensusEncoding for ProofOfWork { } } +impl ConsensusEncodingSized for ProofOfWork { + fn consensus_encode_exact_size(&self) -> usize { + self.pow_algo.as_u64().consensus_encode_exact_size() + self.pow_data.consensus_encode_exact_size() + } +} + impl ConsensusDecoding for ProofOfWork { fn consensus_decode(reader: &mut R) -> Result { let pow_algo = PowAlgorithm::try_from(u64::consensus_decode(reader)?) @@ -109,7 +115,10 @@ impl ConsensusDecoding for ProofOfWork { #[cfg(test)] mod test { - use crate::proof_of_work::proof_of_work::{PowAlgorithm, ProofOfWork}; + use crate::{ + consensus::check_consensus_encoding_correctness, + proof_of_work::proof_of_work::{PowAlgorithm, ProofOfWork}, + }; #[test] fn display() { @@ -125,4 +134,11 @@ mod test { }; assert_eq!(pow.to_bytes(), vec![1]); } + + #[test] + fn consensus_encoding() { + let mut arr = ProofOfWork::new(PowAlgorithm::Monero); + arr.pow_data = vec![1, 2, 3]; + check_consensus_encoding_correctness(arr).unwrap(); + } } diff --git a/base_layer/core/src/transactions/aggregated_body.rs b/base_layer/core/src/transactions/aggregated_body.rs index e4c30d4891..9735c30f4d 100644 --- a/base_layer/core/src/transactions/aggregated_body.rs +++ b/base_layer/core/src/transactions/aggregated_body.rs @@ -47,7 +47,7 @@ use tari_crypto::{ use tari_script::ScriptContext; use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, MaxSizeVec}, + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized, MaxSizeVec}, transactions::{ crypto_factories::CryptoFactories, tari_amount::MicroTari, @@ -578,6 +578,14 @@ impl ConsensusEncoding for AggregateBody { } } +impl ConsensusEncodingSized for AggregateBody { + fn consensus_encode_exact_size(&self) -> usize { + self.inputs.consensus_encode_exact_size() + + self.outputs.consensus_encode_exact_size() + + self.kernels.consensus_encode_exact_size() + } +} + impl ConsensusDecoding for AggregateBody { fn consensus_decode(reader: &mut R) -> Result { const MAX_SIZE: usize = 50000; @@ -588,3 +596,16 @@ impl ConsensusDecoding for AggregateBody { Ok(body) } } + +#[cfg(test)] +mod test { + use tari_common::configuration::Network; + + use crate::{blocks::genesis_block::get_genesis_block, consensus::check_consensus_encoding_correctness}; + + #[test] + fn consensus_encoding() { + let body = get_genesis_block(Network::Esmeralda).block().body.clone(); + check_consensus_encoding_correctness(body).unwrap(); + } +} diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index 4f6978b6e6..19f76b4da3 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -235,6 +235,10 @@ impl TestParams { script![Nop].consensus_encode_exact_size() + output_features.consensus_encode_exact_size(), ) * num_outputs } + + pub fn commit_value(&self, value: MicroTari) -> Commitment { + self.commitment_factory.commit_value(&self.spend_key, value.as_u64()) + } } impl Default for TestParams { diff --git a/base_layer/core/src/transactions/transaction_components/encrypted_value.rs b/base_layer/core/src/transactions/transaction_components/encrypted_value.rs index e28e23c2bf..d061c4afda 100644 --- a/base_layer/core/src/transactions/transaction_components/encrypted_value.rs +++ b/base_layer/core/src/transactions/transaction_components/encrypted_value.rs @@ -136,7 +136,7 @@ impl ConsensusEncoding for EncryptedValue { impl ConsensusEncodingSized for EncryptedValue { fn consensus_encode_exact_size(&self) -> usize { - self.0.len() + SIZE } } @@ -157,7 +157,7 @@ mod test { }; use super::*; - use crate::consensus::ToConsensusBytes; + use crate::consensus::{check_consensus_encoding_correctness, ToConsensusBytes}; #[test] fn it_encodes_to_bytes() { @@ -191,4 +191,10 @@ mod test { assert_eq!(amount, decrypted_value); } } + #[test] + fn consensus_encoding() { + let value = [0u8; SIZE]; + let encrypted_value = EncryptedValue(value); + check_consensus_encoding_correctness(encrypted_value).unwrap(); + } } diff --git a/base_layer/core/src/transactions/transaction_components/output_type.rs b/base_layer/core/src/transactions/transaction_components/output_type.rs index a611c82268..cba40dcbf3 100644 --- a/base_layer/core/src/transactions/transaction_components/output_type.rs +++ b/base_layer/core/src/transactions/transaction_components/output_type.rs @@ -106,6 +106,7 @@ impl Display for OutputType { #[cfg(test)] mod tests { use super::*; + use crate::consensus::check_consensus_encoding_correctness; #[test] fn it_converts_from_byte_to_output_type() { @@ -114,4 +115,10 @@ mod tests { assert_eq!(OutputType::from_byte(2), Some(OutputType::Burn)); assert_eq!(OutputType::from_byte(255), None); } + + #[test] + fn consensus_encoding() { + let t = OutputType::Standard; + check_consensus_encoding_correctness(t).unwrap(); + } } diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs b/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs index 734131e120..b002614013 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs @@ -46,4 +46,13 @@ impl ConsensusDecoding for SideChainFeatures { } #[cfg(test)] -mod tests {} +mod test { + use super::*; + use crate::consensus::check_consensus_encoding_correctness; + + #[test] + fn consensus_encoding() { + let features = SideChainFeatures {}; + check_consensus_encoding_correctness(features).unwrap(); + } +} diff --git a/base_layer/core/src/transactions/transaction_components/transaction_input.rs b/base_layer/core/src/transactions/transaction_components/transaction_input.rs index 96c92a3b25..ccbf137ae0 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_input.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_input.rs @@ -37,7 +37,7 @@ use tari_script::{ExecutionStack, ScriptContext, StackItem, TariScript}; use super::{TransactionInputVersion, TransactionOutputVersion}; use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, DomainSeparatedConsensusHasher}, + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized, DomainSeparatedConsensusHasher}, covenants::Covenant, transactions::{ tari_amount::MicroTari, @@ -484,6 +484,8 @@ impl ConsensusEncoding for TransactionInput { } } +impl ConsensusEncodingSized for TransactionInput {} + impl ConsensusDecoding for TransactionInput { fn consensus_decode(reader: &mut R) -> Result { let version = TransactionInputVersion::consensus_decode(reader)?; @@ -583,3 +585,18 @@ impl ConsensusDecoding for SpentOutput { } } } + +#[cfg(test)] +mod tests { + + use super::*; + use crate::{consensus::check_consensus_encoding_correctness, transactions::test_helpers::create_test_input}; + + #[test] + fn consensus_encoding() { + let factory = CommitmentFactory::default(); + + let (input, _) = create_test_input(123.into(), 321, &factory); + check_consensus_encoding_correctness(input).unwrap(); + } +} diff --git a/base_layer/core/src/transactions/transaction_components/transaction_input_version.rs b/base_layer/core/src/transactions/transaction_components/transaction_input_version.rs index fdb27be87a..f25070c2b4 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_input_version.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_input_version.rs @@ -36,7 +36,7 @@ impl TryFrom for TransactionInputVersion { match value { 0 => Ok(TransactionInputVersion::V0), 1 => Ok(TransactionInputVersion::V1), - _ => Err("Unknown version!".to_string()), + v => Err(format!("Unknown input version {}!", v)), } } } @@ -60,7 +60,7 @@ impl ConsensusDecoding for TransactionInputVersion { reader.read_exact(&mut buf)?; let version = buf[0] .try_into() - .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown version {}", buf[0])))?; + .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown input version {}", buf[0])))?; Ok(version) } } diff --git a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs index e08e8fe799..db4ac1d738 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs @@ -36,7 +36,7 @@ use tari_utilities::{hex::Hex, message_format::MessageFormat}; use super::TransactionKernelVersion; use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, DomainSeparatedConsensusHasher}, + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized, DomainSeparatedConsensusHasher}, transactions::{ tari_amount::MicroTari, transaction_components::{KernelFeatures, TransactionError}, @@ -243,6 +243,8 @@ impl ConsensusEncoding for TransactionKernel { } } +impl ConsensusEncodingSized for TransactionKernel {} + impl ConsensusDecoding for TransactionKernel { fn consensus_decode(reader: &mut R) -> Result { let version = TransactionKernelVersion::consensus_decode(reader)?; @@ -256,3 +258,32 @@ impl ConsensusDecoding for TransactionKernel { Ok(kernel) } } + +#[cfg(test)] +mod tests { + use tari_utilities::ByteArray; + + use super::*; + use crate::{consensus::check_consensus_encoding_correctness, transactions::test_helpers::TestParams}; + + #[test] + fn consensus_encoding() { + let test_params = TestParams::new(); + + let output = TransactionKernel::new( + TransactionKernelVersion::get_current_version(), + KernelFeatures::all(), + MicroTari::from(100), + 123, + test_params.commit_value(321.into()), + Signature::sign( + test_params.spend_key.clone(), + test_params.nonce.clone(), + test_params.nonce.as_bytes(), + ) + .unwrap(), + Some(test_params.commit_value(321.into())), + ); + check_consensus_encoding_correctness(output).unwrap(); + } +} diff --git a/base_layer/core/src/transactions/transaction_components/transaction_kernel_version.rs b/base_layer/core/src/transactions/transaction_components/transaction_kernel_version.rs index 55de3f8664..57656843c9 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_kernel_version.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_kernel_version.rs @@ -32,7 +32,7 @@ impl TryFrom for TransactionKernelVersion { fn try_from(value: u8) -> Result { match value { 0 => Ok(TransactionKernelVersion::V0), - _ => Err("Unknown version!".to_string()), + v => Err(format!("Unknown kernel version {}!", v)), } } } @@ -56,7 +56,7 @@ impl ConsensusDecoding for TransactionKernelVersion { reader.read_exact(&mut buf)?; let version = buf[0] .try_into() - .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown version {}", buf[0])))?; + .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown kernel version {}", buf[0])))?; Ok(version) } } diff --git a/base_layer/core/src/transactions/transaction_components/transaction_output.rs b/base_layer/core/src/transactions/transaction_components/transaction_output.rs index 4b3ebb741d..d1f0004f63 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_output.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_output.rs @@ -477,10 +477,12 @@ impl ConsensusEncoding for TransactionOutput { self.sender_offset_public_key.consensus_encode(writer)?; self.metadata_signature.consensus_encode(writer)?; self.covenant.consensus_encode(writer)?; + self.encrypted_value.consensus_encode(writer)?; self.minimum_value_promise.consensus_encode(writer)?; Ok(()) } } +impl ConsensusEncodingSized for TransactionOutput {} impl ConsensusDecoding for TransactionOutput { fn consensus_decode(reader: &mut R) -> Result { @@ -579,11 +581,14 @@ fn power_of_two_chunk_sizes(len: usize, max_power: u8) -> Vec { #[cfg(test)] mod test { use super::{batch_verify_range_proofs, TransactionOutput}; - use crate::transactions::{ - tari_amount::MicroTari, - test_helpers::{TestParams, UtxoTestParams}, - transaction_components::transaction_output::power_of_two_chunk_sizes, - CryptoFactories, + use crate::{ + consensus::check_consensus_encoding_correctness, + transactions::{ + tari_amount::MicroTari, + test_helpers::{TestParams, UtxoTestParams}, + transaction_components::transaction_output::power_of_two_chunk_sizes, + CryptoFactories, + }, }; #[test] @@ -694,4 +699,13 @@ mod test { output } + + #[test] + fn consensus_encoding() { + let factories = CryptoFactories::default(); + let test_params = TestParams::new(); + + let output = create_valid_output(&test_params, &factories, 123.into(), MicroTari::zero()); + check_consensus_encoding_correctness(output).unwrap(); + } } diff --git a/base_layer/core/src/transactions/transaction_components/transaction_output_version.rs b/base_layer/core/src/transactions/transaction_components/transaction_output_version.rs index 1598c0d766..71802b138d 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_output_version.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_output_version.rs @@ -58,7 +58,7 @@ impl TryFrom for TransactionOutputVersion { match value { 0 => Ok(TransactionOutputVersion::V0), 1 => Ok(TransactionOutputVersion::V1), - _ => Err("Unknown version!".to_string()), + v => Err(format!("Unknown output version {}!", v)), } } } @@ -82,7 +82,7 @@ impl ConsensusDecoding for TransactionOutputVersion { reader.read_exact(&mut buf)?; let version = buf[0] .try_into() - .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown version {}", buf[0])))?; + .map_err(|_| io::Error::new(ErrorKind::InvalidInput, format!("Unknown output version {}", buf[0])))?; Ok(version) } }