From 2a3af27b4fed419a038bac69f5967317d84acc33 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 19 Aug 2022 17:14:28 +0200 Subject: [PATCH] fix!: change monero consensus encoding an update for hardfork v15 (#4492) Description --- Monero-rs has sealed their encoding in the newer version. This means we need to encode the struct using our own encoding trait and only using the encoding for the monero versions. Upgrade to the newest monero v15 hardfork code. Also point towards our own monero-rs fork so we can update code when we want to. --- Cargo.lock | 35 ++++-- .../tari_merge_mining_proxy/src/main.rs | 2 +- .../tari_merge_mining_proxy/src/proxy.rs | 11 +- base_layer/core/Cargo.toml | 2 +- .../core/src/consensus/consensus_encoding.rs | 16 +++ base_layer/core/src/consensus/mod.rs | 1 + .../proof_of_work/monero_rx/fixed_array.rs | 54 ++++---- .../src/proof_of_work/monero_rx/helpers.rs | 33 +++-- .../proof_of_work/monero_rx/merkle_tree.rs | 53 ++++---- .../src/proof_of_work/monero_rx/pow_data.rs | 115 ++++++++---------- base_layer/core/tests/block_validation.rs | 10 +- 11 files changed, 197 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7c97f68b1..f97fb37592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "base58-monero" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d079cdf47e1ca75554200bb2f30bff5a5af16964cac4a566b18de9a5d48db2b" +dependencies = [ + "thiserror", +] + [[package]] name = "base64" version = "0.10.1" @@ -2653,15 +2662,15 @@ dependencies = [ [[package]] name = "monero" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a7038b6ba92588189248fbb4f8b2744d4918a9732f826e414814a50c168dca3" +version = "0.17.2" +source = "git+https://github.com/tari-project/monero-rs.git?branch=main#7aebfd0aa037025cac6cbded3f72d73bf3c18123" dependencies = [ - "base58-monero", + "base58-monero 1.0.0", "curve25519-dalek", "fixed-hash", "hex", "hex-literal", + "sealed", "serde", "serde-big-array", "thiserror", @@ -4144,6 +4153,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sealed" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b5e421024b5e5edfbaa8e60ecf90bda9dbffc602dbb230e6028763f85f0c68c" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "security-framework" version = "2.6.1" @@ -4202,9 +4223,9 @@ dependencies = [ [[package]] name = "serde-big-array" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" +checksum = "3323f09a748af288c3dc2474ea6803ee81f118321775bffa3ac8f7e65c5e90e7" dependencies = [ "serde", ] @@ -5312,7 +5333,7 @@ name = "tari_utilities" version = "0.4.5" source = "git+https://github.com/tari-project/tari_utilities.git?tag=v0.4.5#a9e059ba84f8d931aeaa2b88a52a1ce6503b2deb" dependencies = [ - "base58-monero", + "base58-monero 0.3.2", "base64 0.13.0", "bincode", "newtype-ops", diff --git a/applications/tari_merge_mining_proxy/src/main.rs b/applications/tari_merge_mining_proxy/src/main.rs index 90b6e25a17..bb5ea7992d 100644 --- a/applications/tari_merge_mining_proxy/src/main.rs +++ b/applications/tari_merge_mining_proxy/src/main.rs @@ -75,7 +75,7 @@ async fn main() -> Result<(), anyhow::Error> { let config = MergeMiningProxyConfig::load_from(&cfg)?; - error!(target: LOG_TARGET, "Configuration: {:?}", config); + info!(target: LOG_TARGET, "Configuration: {:?}", config); let client = reqwest::Client::builder() .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(10)) diff --git a/applications/tari_merge_mining_proxy/src/proxy.rs b/applications/tari_merge_mining_proxy/src/proxy.rs index c5784e1dd8..e67c7fbd76 100644 --- a/applications/tari_merge_mining_proxy/src/proxy.rs +++ b/applications/tari_merge_mining_proxy/src/proxy.rs @@ -41,11 +41,9 @@ use jsonrpc::error::StandardError; use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; use tari_app_grpc::tari_rpc as grpc; -use tari_core::proof_of_work::{ - monero_difficulty, - monero_rx, - monero_rx::FixedByteArray, - randomx_factory::RandomXFactory, +use tari_core::{ + consensus::ConsensusEncoding, + proof_of_work::{monero_difficulty, monero_rx, monero_rx::FixedByteArray, randomx_factory::RandomXFactory}, }; use tari_utilities::hex::Hex; use tracing::{debug, error, info, instrument, trace, warn}; @@ -269,8 +267,7 @@ impl InnerService { let header_mut = block_data.tari_block.header.as_mut().unwrap(); let height = header_mut.height; - header_mut.pow.as_mut().unwrap().pow_data = monero_rx::serialize(&monero_data); - + monero_data.consensus_encode(&mut header_mut.pow.as_mut().unwrap().pow_data)?; let tari_header = header_mut.clone().try_into().map_err(MmProxyError::ConversionError)?; let mut base_node_client = self.base_node_client.clone(); let start = Instant::now(); diff --git a/base_layer/core/Cargo.toml b/base_layer/core/Cargo.toml index 9ad67b86bc..a4123f56d1 100644 --- a/base_layer/core/Cargo.toml +++ b/base_layer/core/Cargo.toml @@ -54,7 +54,7 @@ integer-encoding = "3.0.2" lmdb-zero = "0.4.4" log = "0.4" log-mdc = "0.1.0" -monero = { version = "^0.13.0", features = ["serde_support"], optional = true } +monero = { git = "https://github.com/tari-project/monero-rs.git", branch = "main" , features = ["serde"], optional = true } newtype-ops = "0.1.4" num-traits = "0.2.15" num-derive = "0.3.3" diff --git a/base_layer/core/src/consensus/consensus_encoding.rs b/base_layer/core/src/consensus/consensus_encoding.rs index 98b053f747..767ae65cfd 100644 --- a/base_layer/core/src/consensus/consensus_encoding.rs +++ b/base_layer/core/src/consensus/consensus_encoding.rs @@ -76,6 +76,22 @@ impl ToConsensusBytes fo } } +pub trait FromConsensusBytes +where T: ConsensusDecoding + ?Sized +{ + fn from_consensus_bytes(bytes: &[u8]) -> io::Result; +} + +impl FromConsensusBytes for T { + fn from_consensus_bytes(mut bytes: &[u8]) -> io::Result { + let decoded = Self::consensus_decode(&mut bytes)?; + if !bytes.is_empty() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "Extra bytes at end of data")); + } + Ok(decoded) + } +} + #[cfg(test)] pub mod test { use super::*; diff --git a/base_layer/core/src/consensus/mod.rs b/base_layer/core/src/consensus/mod.rs index 806ac13211..cff83e7f5f 100644 --- a/base_layer/core/src/consensus/mod.rs +++ b/base_layer/core/src/consensus/mod.rs @@ -38,6 +38,7 @@ pub use consensus_encoding::{ ConsensusEncodingSized, ConsensusHasher, DomainSeparatedConsensusHasher, + FromConsensusBytes, MaxSizeBytes, MaxSizeVec, ToConsensusBytes, 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 9ec1970cd4..c27495bffe 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 @@ -20,14 +20,17 @@ // 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::{convert::TryFrom, io, ops::Deref}; - -use monero::{ - consensus::{encode, Decodable, Encodable}, - VarInt, +use std::{ + convert::TryFrom, + io, + io::{Read, Write}, + ops::Deref, }; + use tari_utilities::{ByteArray, ByteArrayError}; +use crate::consensus::{ConsensusDecoding, ConsensusEncoding}; + const MAX_ARR_SIZE: usize = 63; #[derive(Clone, Debug)] @@ -109,33 +112,38 @@ impl ByteArray for FixedByteArray { } } -impl Decodable for FixedByteArray { - fn consensus_decode(d: &mut D) -> Result { - #[allow(clippy::cast_possible_truncation)] - let len = VarInt::consensus_decode(d)?.0 as usize; +impl ConsensusDecoding for FixedByteArray { + fn consensus_decode(reader: &mut R) -> Result { + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + let len = buf[0] as usize; if len > MAX_ARR_SIZE { - return Err(encode::Error::ParseFailed( - "length exceeded maximum of 64-bytes for FixedByteArray", + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("length exceeded maximum of 64-bytes for FixedByteArray: {}", len), )); } let mut ret = FixedByteArray::new(); for _ in 0..len { // PANIC: Cannot happen because len has been checked - ret.push(Decodable::consensus_decode(d)?); + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + ret.push(buf[0]); } Ok(ret) } } -impl Encodable for FixedByteArray { - fn consensus_encode(&self, e: &mut E) -> Result { - self.as_slice().consensus_encode(e) +impl ConsensusEncoding for FixedByteArray { + fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { + writer.write_all(&[self.len][..])?; + writer.write_all(&self.elems[0..self.len()])?; + Ok(()) } } #[cfg(test)] mod test { - use monero::consensus; use tari_utilities::hex::Hex; use super::*; @@ -167,9 +175,11 @@ mod test { #[test] fn serialize_deserialize() { - let data = consensus::serialize(&FixedByteArray::from_hex("ffffffffffffffffffffffffff").unwrap()); + 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 = consensus::deserialize::(&data).unwrap(); + let arr = FixedByteArray::consensus_decode(&mut data.as_slice()).unwrap(); assert!(arr.iter().all(|b| *b == 0xff)); } @@ -181,19 +191,17 @@ mod test { assert_eq!(arr.len(), MAX_ARR_SIZE); buf[0] = 64; - let err = FixedByteArray::consensus_decode(&mut io::Cursor::new(buf)).unwrap_err(); - assert!(matches!(err, encode::Error::ParseFailed(_))); + let _err = FixedByteArray::consensus_decode(&mut io::Cursor::new(buf)).unwrap_err(); // VarInt encoding that doesnt terminate, but _would_ represent a number < MAX_ARR_SIZE buf[0] = 0b1000000; buf[1] = 0b1000000; - let err = FixedByteArray::consensus_decode(&mut io::Cursor::new(buf)).unwrap_err(); - assert!(matches!(err, encode::Error::ParseFailed(_))); + let _err = FixedByteArray::consensus_decode(&mut io::Cursor::new(buf)).unwrap_err(); } #[test] fn capacity_overflow_does_not_panic() { let data = &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]; - let _result = consensus::deserialize::(data); + let _result = FixedByteArray::consensus_decode(&mut data.as_slice()).unwrap_err(); } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index fc412dfef9..5b7449a211 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -199,7 +199,10 @@ mod test { }; use super::*; - use crate::proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}; + use crate::{ + consensus::ConsensusEncoding, + proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}, + }; // This tests checks the hash of monero-rs #[test] @@ -319,7 +322,8 @@ mod test { coinbase_merkle_proof, coinbase_tx: block.miner_tx, }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -379,7 +383,8 @@ mod test { coinbase_merkle_proof, coinbase_tx: block.miner_tx, }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -426,7 +431,9 @@ mod test { coinbase_merkle_proof, coinbase_tx: block.miner_tx, }; - let serialized = consensus::serialize(&monero_data); + + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -459,7 +466,7 @@ mod test { nonce: 0, pow: ProofOfWork::default(), }; - let hash = Hash::null_hash(); + let hash = Hash::null(); append_merge_mining_tag(&mut block, hash).unwrap(); let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); let mut hashes = Vec::with_capacity(count as usize); @@ -480,7 +487,8 @@ mod test { coinbase_merkle_proof, coinbase_tx: block.miner_tx, }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -534,7 +542,8 @@ mod test { coinbase_merkle_proof, coinbase_tx: Default::default(), }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -568,10 +577,11 @@ mod test { randomx_key: FixedByteArray::default(), transaction_count: 1, merkle_root: Default::default(), - coinbase_merkle_proof: create_merkle_proof(&[Hash::null_hash()], &Hash::null_hash()).unwrap(), + coinbase_merkle_proof: create_merkle_proof(&[Hash::null()], &Hash::null()).unwrap(), coinbase_tx: Default::default(), }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, @@ -621,11 +631,12 @@ mod test { header: block.header, randomx_key: FixedByteArray::from_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), transaction_count: count, - merkle_root: Hash::null_hash(), + merkle_root: Hash::null(), coinbase_merkle_proof, coinbase_tx: block.miner_tx, }; - let serialized = consensus::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); let pow = ProofOfWork { pow_algo: PowAlgorithm::Monero, pow_data: serialized, 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 6a0887e2fb..45787be3bd 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 @@ -24,18 +24,24 @@ //! //! See -use std::io; +use std::{ + io, + io::{Read, Write}, +}; use monero::{ - consensus::{encode, Decodable, Encodable}, + consensus::{Decodable, Encodable}, Hash, }; -use crate::proof_of_work::monero_rx::error::MergeMineError; +use crate::{ + consensus::{ConsensusDecoding, ConsensusEncoding}, + proof_of_work::monero_rx::error::MergeMineError, +}; /// Returns the Keccak 256 hash of the byte input fn cn_fast_hash(data: &[u8]) -> Hash { - Hash::hash(data) + Hash::new(data) } /// Returns the Keccak 256 hash of 2 hashes @@ -85,7 +91,7 @@ pub fn tree_hash(hashes: &[Hash]) -> Result { 2 => Ok(cn_fast_hash2(&hashes[0], &hashes[1])), n => { let mut cnt = tree_hash_count(n)?; - let mut buf = vec![Hash::null_hash(); cnt]; + let mut buf = vec![Hash::null(); cnt]; // c is the number of elements between the number of hashes and the next power of 2. let c = 2 * cnt - hashes.len(); @@ -165,29 +171,34 @@ impl MerkleProof { impl Default for MerkleProof { fn default() -> Self { Self { - branch: vec![Hash::null_hash()], + branch: vec![Hash::null()], depth: 0, path_bitmap: 0, } } } -impl Decodable for MerkleProof { - fn consensus_decode(d: &mut D) -> Result { +impl ConsensusDecoding for MerkleProof { + fn consensus_decode(reader: &mut R) -> Result { Ok(Self { - branch: Decodable::consensus_decode(d)?, - depth: Decodable::consensus_decode(d)?, - path_bitmap: Decodable::consensus_decode(d)?, + branch: Decodable::consensus_decode(reader).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Could not decode Monero branch {}", e), + ) + })?, + depth: ConsensusDecoding::consensus_decode(reader)?, + path_bitmap: ConsensusDecoding::consensus_decode(reader)?, }) } } -impl Encodable for MerkleProof { - fn consensus_encode(&self, e: &mut E) -> Result { - let mut len = self.branch.consensus_encode(e)?; - len += self.depth.consensus_encode(e)?; - len += self.path_bitmap.consensus_encode(e)?; - Ok(len) +impl ConsensusEncoding for MerkleProof { + fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { + let _ = self.branch.consensus_encode(writer)?; + ConsensusEncoding::consensus_encode(&self.depth, writer)?; + ConsensusEncoding::consensus_encode(&self.path_bitmap, writer)?; + Ok(()) } } @@ -215,7 +226,7 @@ pub fn create_merkle_proof(hashes: &[Hash], hash: &Hash) -> Option let mut idx = hashes.iter().position(|node| node == hash)?; let mut count = tree_hash_count(len).ok()?; - let mut ints = vec![Hash::null_hash(); count]; + let mut ints = vec![Hash::null(); count]; let c = 2 * count - len; ints[..c].copy_from_slice(&hashes[..c]); @@ -457,7 +468,7 @@ mod test { #[test] fn empty_hashset_has_no_proof() { - assert!(create_merkle_proof(&[], &Hash::null_hash()).is_none()); + assert!(create_merkle_proof(&[], &Hash::null()).is_none()); } #[test] @@ -469,7 +480,7 @@ mod test { assert_eq!(proof.calculate_root(&tx_hashes[0]), tx_hashes[0]); assert_eq!(proof.branch(), tx_hashes); - assert!(create_merkle_proof(&tx_hashes[..], &Hash::null_hash()).is_none()); + assert!(create_merkle_proof(&tx_hashes[..], &Hash::null()).is_none()); } #[test] @@ -491,7 +502,7 @@ mod test { assert_eq!(proof.branch()[0], tx_hashes[0]); assert_eq!(proof.calculate_root(&tx_hashes[1]), expected_root); - assert!(create_merkle_proof(tx_hashes, &Hash::null_hash()).is_none()); + assert!(create_merkle_proof(tx_hashes, &Hash::null()).is_none()); } #[test] 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 da453a5fcd..d6101c017d 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 @@ -24,16 +24,21 @@ use std::{ fmt, fmt::{Display, Formatter}, io, + io::{Read, Write}, }; use monero::{ - consensus::{encode, Decodable, Encodable}, + consensus::{Decodable, Encodable}, cryptonote::hash::Hashable, }; use tari_utilities::hex::{to_hex, Hex}; -use super::{deserialize, error::MergeMineError, fixed_array::FixedByteArray, merkle_tree::MerkleProof}; -use crate::{blocks::BlockHeader, proof_of_work::monero_rx::helpers::create_block_hashing_blob}; +use super::{error::MergeMineError, fixed_array::FixedByteArray, merkle_tree::MerkleProof}; +use crate::{ + blocks::BlockHeader, + consensus::{ConsensusDecoding, ConsensusEncoding, FromConsensusBytes}, + proof_of_work::monero_rx::helpers::create_block_hashing_blob, +}; /// This is a struct to deserialize the data from he pow field into data required for the randomX Monero merged mine /// pow. @@ -56,7 +61,8 @@ pub struct MoneroPowData { impl MoneroPowData { pub fn from_header(tari_header: &BlockHeader) -> Result { - deserialize(&tari_header.pow.pow_data).map_err(|e| MergeMineError::DeserializeError(format!("{:?}", e))) + MoneroPowData::from_consensus_bytes(tari_header.pow.pow_data.as_slice()) + .map_err(|e| MergeMineError::DeserializeError(format!("{:?}", e))) } /// Returns true if the coinbase merkle proof produces the `merkle_root` hash, otherwise false @@ -85,49 +91,64 @@ impl Display for MoneroPowData { } } -impl Decodable for MoneroPowData { - fn consensus_decode(d: &mut D) -> Result { +impl ConsensusDecoding for MoneroPowData { + fn consensus_decode(reader: &mut R) -> Result { Ok(Self { - header: Decodable::consensus_decode(d)?, - randomx_key: Decodable::consensus_decode(d)?, - transaction_count: Decodable::consensus_decode(d)?, - merkle_root: Decodable::consensus_decode(d)?, - coinbase_merkle_proof: Decodable::consensus_decode(d)?, - coinbase_tx: Decodable::consensus_decode(d)?, + header: Decodable::consensus_decode(reader).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Could not decode Monero header {}", e), + ) + })?, + randomx_key: ConsensusDecoding::consensus_decode(reader)?, + transaction_count: ConsensusDecoding::consensus_decode(reader)?, + merkle_root: Decodable::consensus_decode(reader).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Could not decode Monero merkle header {}", e), + ) + })?, + coinbase_merkle_proof: ConsensusDecoding::consensus_decode(reader)?, + coinbase_tx: Decodable::consensus_decode(reader).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Could not decode Monero coinbase transaction {}", e), + ) + })?, }) } } -impl Encodable for MoneroPowData { - fn consensus_encode(&self, e: &mut E) -> Result { - let mut len = self.header.consensus_encode(e)?; - len += self.randomx_key.consensus_encode(e)?; - len += self.transaction_count.consensus_encode(e)?; - - len += self.merkle_root.consensus_encode(e)?; - len += self.coinbase_merkle_proof.consensus_encode(e)?; - len += self.coinbase_tx.consensus_encode(e)?; - Ok(len) +impl ConsensusEncoding for MoneroPowData { + fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { + let _ = 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.coinbase_merkle_proof.consensus_encode(writer)?; + let _ = self.coinbase_tx.consensus_encode(writer)?; + Ok(()) } } #[cfg(test)] mod test { - use monero::consensus; use tari_utilities::hex::from_hex; use super::*; - const POW_DATA_BLOB: &str = "0e0eff8a828606e62827cbb1c8f13eeddaae1d2c5dbb36c12a3d30d20d20b35a540bdba9d8e162604a0000202378cf4e85ef9a0629719e228c8c9807575469c3f45b3710c7960079a5dfdd661600b3cdc310a8f619ea2feadb178021ea0b853caa2f41749f7f039dcd4102d24f0504b4d72f22ca81245c538371a07331546cbd9935068637166d9cd627c521fb0e98d6161a7d971ee608b2b93719327d1cf5f95f9cc15beab7c6fb0894205c9218e4f9810873976eaf62d53ce631e8ad37bbaacc5da0267cd38342d66bdecce6541bb5c761b8ff66e7f6369cd3b0c2cb106a325c7342603516c77c9dcbb67388128a04000000000002fd873401ffc1873401c983eae58cd001026eb5be712030e2d49c9329f7f578325daa8ad7296a58985131544d8fe8a24c934d01ad27b94726423084ffc0f7eda31a8c9691836839c587664a036c3986b33f568f020861f4f1c2c37735680300916c27a920e462fbbfce5ac661ea9ef91fc78d620c61c43d5bb6a9644e3c17e000"; + const POW_DATA_BLOB: &str = "1010989af89706d7fc36490967c52552f5f970b3e71857145426d55f19a0f291aad87fe3949ca7ab2b03002098a7ff37940ab2a8199192b6468d7704b1a46b37aa533298c8b020c2945f36485088afcd6c40c6d6b5fba15ffc256d7bdfdc7879e98287803d9602752df500e35b066d1cf333fcce964b72063915f082d730c708859a0e9288241bfdd9c3c6b471a432a8434282ada7df2675826e086c85b0085bef38b88f2984790553d4925e74f445cc42a810ed9ae296f7e105e5da77e8c58c51fe3e6f1b122c94ae2e27ecffff8511d9dc3554b49d41c9acdaccab04452126e4e2d897d09d49a794e192cd51b76b52628bed70ddb8a3f755035e4e6f23eda8e01e5af885f07c5e5ec742307c88f4446cf32225f52bf019ef198fa2f3957937b6ba96366c731ee47212be92ac5e06000292a9a40101ffd6a8a40101b9f998fcd81103502bb7087b807c5f4fec15891983ac05d05412e5900ca47e6bdf31d7e2c55082574d01ffb9bb5f384f2725a21e36b44fb100791f7259066d7982d616950981e9ce77010208e74c7cee8930e6800300020c4db762c76a89966cebe345f55f725a59c6cbba8630cc0b6bae388718dd1f00"; #[test] fn consensus_serialization() { let bytes = from_hex(POW_DATA_BLOB).unwrap(); - let data = consensus::deserialize::(&bytes).expect("If this fails then consensus has changed"); - assert_eq!(data.transaction_count, 22); - assert_eq!(data.coinbase_merkle_proof.branch().len(), 4); - assert_eq!(bytes.len(), 374); - let ser = consensus::serialize(&data); + let data = + MoneroPowData::from_consensus_bytes(bytes.as_slice()).expect("If this fails then consensus has changed"); + assert_eq!(data.transaction_count, 80); + assert_eq!(data.coinbase_merkle_proof.branch().len(), 6); + assert_eq!(bytes.len(), 435); + let mut ser = Vec::new(); + data.consensus_encode(&mut ser).unwrap(); assert_eq!(ser, bytes); } @@ -135,41 +156,11 @@ mod test { fn consensus_deserialize_reject_extra_bytes() { let mut bytes = from_hex(POW_DATA_BLOB).unwrap(); bytes.extend(&[0u8; 10]); - let err = consensus::deserialize::(&bytes).unwrap_err(); - // ParseFailed("data not consumed entirely when explicitly deserializing") - assert!(matches!(err, encode::Error::ParseFailed(_))); + + let _err = MoneroPowData::from_consensus_bytes(bytes.as_slice()).unwrap_err(); let mut bytes = from_hex(POW_DATA_BLOB).unwrap(); bytes.push(1); - let err = consensus::deserialize::(&bytes).unwrap_err(); - assert!(matches!(err, encode::Error::ParseFailed(_))); - } - - mod fuzz { - use monero::TxIn; - - use super::*; - - #[test] - #[should_panic(expected = "capacity overflow")] - fn simple_capacity_overflow_panic() { - let data = &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]; - let _result = deserialize::>(data); - } - - #[test] - #[should_panic(expected = "capacity overflow")] - fn panic_alloc_capacity_overflow_moneroblock_deserialize() { - let data = [ - 0x0f, 0x9e, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x08, 0x9e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0xe7, 0xaa, 0xfd, 0x8b, 0x47, 0x06, 0x8d, 0xed, 0xe3, 0x00, 0xed, 0x44, 0xfc, 0x77, 0xd6, 0x58, - 0xf6, 0xf2, 0x69, 0x06, 0x8d, 0xed, 0xe3, 0x00, 0xed, 0x44, 0xfc, 0x77, 0xd6, 0x58, 0xf6, 0xf2, 0x69, - 0x62, 0x38, 0xdb, 0x5e, 0x4d, 0x6d, 0x9c, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, - 0x8f, 0x74, 0x3c, 0xb3, 0x1b, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let _result = deserialize::(&data); - } + let _err = MoneroPowData::from_consensus_bytes(bytes.as_slice()).unwrap_err(); } } diff --git a/base_layer/core/tests/block_validation.rs b/base_layer/core/tests/block_validation.rs index 630e84e600..c2ea9271f9 100644 --- a/base_layer/core/tests/block_validation.rs +++ b/base_layer/core/tests/block_validation.rs @@ -28,7 +28,12 @@ use tari_common::configuration::Network; use tari_core::{ blocks::{Block, BlockHeaderAccumulatedData, BlockHeaderValidationError, BlockValidationError, ChainBlock}, chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, ChainStorageError, Validators}, - consensus::{consensus_constants::PowAlgorithmConstants, ConsensusConstantsBuilder, ConsensusManager}, + consensus::{ + consensus_constants::PowAlgorithmConstants, + ConsensusConstantsBuilder, + ConsensusEncoding, + ConsensusManager, + }, proof_of_work::{ monero_rx, monero_rx::{FixedByteArray, MoneroPowData}, @@ -187,7 +192,8 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { coinbase_merkle_proof, coinbase_tx: mblock.miner_tx, }; - let serialized = monero_rx::serialize(&monero_data); + let mut serialized = Vec::new(); + monero_data.consensus_encode(&mut serialized).unwrap(); tblock.header.pow.pow_algo = PowAlgorithm::Monero; tblock.header.pow.pow_data = serialized; }