Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce serde_generic_array #1434

Merged
merged 9 commits into from
Mar 4, 2022
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions blockchain/blocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ base64 = "0.13"
test_utils = { version = "0.1.0", path = "../../utils/test_utils/", features = ["test_constructors"] }
hex = "0.4.2"
serde_json = "1.0"
serde_cbor = { version = "0.11", features = ["tags"] }
async-std = "1.9"

[features]
Expand Down
123 changes: 119 additions & 4 deletions blockchain/blocks/src/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use clock::ChainEpoch;
use crypto::Signature;
use derive_builder::Builder;
use encoding::blake2b_256;
use encoding::{Cbor, Error as EncodingError};
use encoding::{serde_generic_array::check_length, Cbor, Error as EncodingError, SerdeError};
use fil_types::{PoStProof, BLOCKS_PER_EPOCH};
use num_bigint::{
bigint_ser::{BigIntDe, BigIntSer},
BigInt,
};
use once_cell::sync::OnceCell;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
use sha2::Digest;
use std::fmt;
use vm::TokenAmount;
Expand Down Expand Up @@ -157,6 +157,9 @@ impl Serialize for BlockHeader {
where
S: Serializer,
{
encoding::check_generic_array_length!(&self.beacon_entries, &self.winning_post_proof)
.map_err(ser::Error::custom)?;

(
&self.miner_address,
&self.ticket,
Expand Down Expand Up @@ -225,6 +228,9 @@ impl<'de> Deserialize<'de> for BlockHeader {
is_validated: Default::default(),
};

encoding::check_generic_array_length!(&header.beacon_entries, &header.winning_post_proof)
.map_err(de::Error::custom)?;

Ok(header)
}
}
Expand Down Expand Up @@ -457,10 +463,14 @@ impl fmt::Display for BlockHeader {

#[cfg(test)]
mod tests {
use crate::{errors::Error, BlockHeader};
use crate::{errors::Error, header::BigIntSer, BlockHeader};
use address::Address;
use beacon::{BeaconEntry, BeaconPoint, BeaconSchedule, MockBeacon};
use encoding::Cbor;
use encoding::{from_slice, to_vec, Cbor, SerdeError, GENERIC_ARRAY_MAX_LEN};
use fil_types::{PoStProof, RegisteredPoStProof};

use serde::Serialize;
use serde_cbor::ser::Serializer;

use std::sync::Arc;
use std::time::Duration;
Expand All @@ -482,6 +492,7 @@ mod tests {
.unwrap())
.unwrap();
}

#[test]
fn beacon_entry_exists() {
// Setup
Expand Down Expand Up @@ -513,4 +524,108 @@ mod tests {
}
}
}

#[test]
fn can_serialize_and_deserialize() {
for len in [0, 1, GENERIC_ARRAY_MAX_LEN] {
let block_header = BlockHeader::builder()
.miner_address(Address::new_id(0))
.beacon_entries(vec![Default::default(); len])
.winning_post_proof(vec![
PoStProof {
post_proof: RegisteredPoStProof::StackedDRGWindow2KiBV1,
proof_bytes: Default::default(),
};
len
])
.build()
.unwrap();

let encoding = to_vec(&block_header).unwrap();
assert!(from_slice::<BlockHeader>(&encoding).is_ok());
}
}

#[test]
fn cannot_serialize_overflowed_generic_array() {
let len = GENERIC_ARRAY_MAX_LEN + 1;
let overflowed_beacon_entries = BlockHeader::builder()
.miner_address(Address::new_id(0))
.beacon_entries(vec![Default::default(); len])
.build()
.unwrap();

let overflowed_winning_post_proof = BlockHeader::builder()
.miner_address(Address::new_id(0))
.winning_post_proof(vec![
PoStProof {
post_proof: RegisteredPoStProof::StackedDRGWindow2KiBV1,
proof_bytes: Default::default(),
};
len
])
.build()
.unwrap();

for header in [overflowed_beacon_entries, overflowed_winning_post_proof] {
assert_eq!(
to_vec(&header).err().unwrap().to_string(),
SerdeError::GenericArrayExceedsMaxLength(len).to_string(),
);
}
}

#[test]
fn cannot_deserialize_overflowed_generic_array() {
let len = GENERIC_ARRAY_MAX_LEN + 1;
let overflowed_beacon_entries = BlockHeader::builder()
.miner_address(Address::new_id(0))
.beacon_entries(vec![Default::default(); len])
.build()
.unwrap();

let overflowed_winning_post_proof = BlockHeader::builder()
.miner_address(Address::new_id(0))
.winning_post_proof(vec![
PoStProof {
post_proof: RegisteredPoStProof::StackedDRGWindow2KiBV1,
proof_bytes: Default::default(),
};
len
])
.build()
.unwrap();

for header in [overflowed_beacon_entries, overflowed_winning_post_proof] {
let mut encoding = Vec::new();
(
&header.miner_address,
&header.ticket,
&header.election_proof,
&header.beacon_entries,
&header.winning_post_proof,
&header.parents,
BigIntSer(&header.weight),
&header.epoch,
&header.state_root,
&header.message_receipts,
&header.messages,
&header.bls_aggregate,
&header.timestamp,
&header.signature,
&header.fork_signal,
BigIntSer(&header.parent_base_fee),
)
.serialize(&mut Serializer::new(&mut encoding))
.unwrap();

assert_eq!(
from_slice::<BlockHeader>(&encoding)
.err()
.unwrap()
.to_string(),
SerdeError::GenericArrayExceedsMaxLength(len).to_string(),
);
}
}
}
2 changes: 2 additions & 0 deletions blockchain/blocks/src/tipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use super::{Block, BlockHeader, Error, Ticket};
use cid::Cid;
use clock::ChainEpoch;
use encoding::serde_generic_array;
use encoding::Cbor;
use num_bigint::BigInt;
use once_cell::sync::OnceCell;
Expand All @@ -15,6 +16,7 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TipsetKeys {
#[serde(with = "serde_generic_array")]
pub cids: Vec<Cid>,
}

Expand Down
16 changes: 16 additions & 0 deletions encoding/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::{BYTE_ARRAY_MAX_LEN, GENERIC_ARRAY_MAX_LEN};
use cid::Error as CidError;
use serde_cbor::error::Error as CborError;
use std::fmt;
use std::io;
use thiserror::Error;

/// Error type for serializing and deserializing data via serde
#[derive(Debug, PartialEq, Error)]
pub enum SerdeError {
#[error(
"Byte array exceeds max length `{0}`, (expected <= {})",
BYTE_ARRAY_MAX_LEN
)]
ByteArrayExceedsMaxLength(usize),
#[error(
"Generic array exceeds max length `{0}`, (expected <= {})",
GENERIC_ARRAY_MAX_LEN
)]
GenericArrayExceedsMaxLength(usize),
}

/// Error type for encoding and decoding data through any Forest supported protocol.
///
/// This error will provide any details about the data which was attempted to be
Expand Down
4 changes: 2 additions & 2 deletions encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

mod bytes;
mod cbor;
mod checked_serde_bytes;
mod errors;
mod hash;

pub mod serde_byte_array;
pub use serde::{de, ser};
pub use serde_bytes;
pub use serde_cbor::{error, from_reader, from_slice, tags, to_vec, to_writer};
pub mod serde_generic_array;

pub use self::bytes::*;
pub use self::cbor::*;
pub use self::checked_serde_bytes::serde_byte_array;
pub use self::errors::*;
pub use self::hash::*;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::BYTE_ARRAY_MAX_LEN;
use crate::{errors::SerdeError, BYTE_ARRAY_MAX_LEN};
use serde::{de, ser, Deserializer, Serializer};
use serde_bytes::{Deserialize, Serialize};

/// serde_bytes with max length check
pub mod serde_byte_array {
use super::*;
/// checked if input > `crate::BYTE_ARRAY_MAX_LEN`
pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: ?Sized + Serialize + AsRef<[u8]>,
S: Serializer,
{
let len = bytes.as_ref().len();
if len > BYTE_ARRAY_MAX_LEN {
return Err(ser::Error::custom(SerdeError::ByteArrayExceedsMaxLength(
len,
)));
}

Serialize::serialize(bytes, serializer)
}

/// checked if input > `crate::BYTE_ARRAY_MAX_LEN`
pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: ?Sized + Serialize + AsRef<[u8]>,
S: Serializer,
{
/// checked if output > `crate::ByteArrayMaxLen`
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de> + AsRef<[u8]>,
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).and_then(|bytes: T| {
let len = bytes.as_ref().len();
if len > BYTE_ARRAY_MAX_LEN {
return Err(ser::Error::custom::<String>(
"Array exceed max length".into(),
));
Err(de::Error::custom(SerdeError::ByteArrayExceedsMaxLength(
len,
)))
} else {
Ok(bytes)
}

Serialize::serialize(bytes, serializer)
}

/// checked if output > `crate::ByteArrayMaxLen`
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de> + AsRef<[u8]>,
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).and_then(|bytes: T| {
if bytes.as_ref().len() > BYTE_ARRAY_MAX_LEN {
Err(de::Error::custom::<String>(
"Array exceed max length".into(),
))
} else {
Ok(bytes)
}
})
}
})
}

#[cfg(test)]
mod tests {
use super::serde_byte_array;
use crate::BYTE_ARRAY_MAX_LEN;
use crate::{serde_byte_array, SerdeError, BYTE_ARRAY_MAX_LEN};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
Expand All @@ -68,13 +63,14 @@ mod tests {

#[test]
fn cannot_serialize_byte_array_overflow() {
let len = BYTE_ARRAY_MAX_LEN + 1;
let bytes = ByteArray {
inner: vec![0; BYTE_ARRAY_MAX_LEN + 1],
inner: vec![0; len],
};

assert_eq!(
format!("{}", serde_cbor::to_vec(&bytes).err().unwrap()),
"Array exceed max length".to_string()
serde_cbor::to_vec(&bytes).err().unwrap().to_string(),
LesnyRumcajs marked this conversation as resolved.
Show resolved Hide resolved
SerdeError::ByteArrayExceedsMaxLength(len).to_string()
);
}

Expand Down Expand Up @@ -106,13 +102,11 @@ mod tests {
overflow_encoding.push(0);

assert_eq!(
format!(
"{}",
serde_cbor::from_slice::<ByteArray>(&overflow_encoding)
.err()
.unwrap()
),
"Array exceed max length".to_string()
serde_cbor::from_slice::<ByteArray>(&overflow_encoding)
.err()
.unwrap()
.to_string(),
SerdeError::ByteArrayExceedsMaxLength(BYTE_ARRAY_MAX_LEN + 1).to_string()
);
}
}
Loading