Repository: paritytech/parity-common (rust)

Issue:
Drop support for databases repair attempt.
This comes from https://github.com/openethereum/openethereum/issues/264.
Currently, if the database is corrupted, kvdb-Rocksdb will attempt to repair it using the code from leveldb.

As the code only works on single column databases, it’s no longer effective and just lur users into possible rescue which won’t happen. This should be removed and replaced with an option to trim the database (from the beginning to the specified block possibly the latest valid block in the case of sst corruption).

Pull request:
kvdb-rocksdb: do not attempt to repair
Closes #666.

Apparently, it never worked. :/

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bbe6e0c..961bb36 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,9 +4,7 @@ parity-common welcomes contribution from everyone in the form of suggestions, bu
 reports, pull requests, and feedback. This document gives some guidance if you
 are thinking of helping us.
 
-Please reach out here in a GitHub issue or in the parity channel on [gitter] if we can do anything to help you contribute.
-
-[gitter]: https://gitter.im/paritytech/parity
+Please reach out here in a GitHub issue if we can do anything to help you contribute.
 
 ## Submitting bug reports and feature requests
 
@@ -24,7 +22,7 @@ solve with the feature, any ideas for how parity-common could support solving th
 
 As many crates in the rust ecosystem, all crates in parity-common follow [semantic versioning]. This means bumping PATCH version on bug fixes that don't break backwards compatibility, MINOR version on new features and MAJOR version otherwise (MAJOR.MINOR.PATCH). Versions < 1.0 are considered to have the format 0.MAJOR.MINOR, which means bumping MINOR version for all non-breaking changes.
 
-If you bump a dependency that is publicly exposed in a crate's API (e.g. `pub use dependency;` or `pub field: dependency::Dependency`) and the version transition for the dependency was semver-breaking, then it is considered to be a breaking change for the consuming crate as well. To put it simply, if your change could cause a compilation error in user's code, it is a breaking change.
+For checking whether a change is SemVer-breaking, please refer to https://doc.rust-lang.org/cargo/reference/semver.html.
 
 Bumping versions should be done in a separate from regular code changes PR.
 
diff --git a/ethbloom/Cargo.toml b/ethbloom/Cargo.toml
index 95a84ee..df2b546 100644
--- a/ethbloom/Cargo.toml
+++ b/ethbloom/Cargo.toml
@@ -14,7 +14,7 @@ rust-version = "1.56.1"
 tiny-keccak = { version = "2.0", features = ["keccak"] }
 crunchy = { version = "0.2.2", default-features = false, features = ["limit_256"] }
 fixed-hash = { path = "../fixed-hash", version = "0.7", default-features = false }
-impl-serde = { path = "../primitive-types/impls/serde", version = "0.3", default-features = false, optional = true }
+impl-serde = { path = "../primitive-types/impls/serde", version = "0.4", default-features = false, optional = true }
 impl-rlp = { path = "../primitive-types/impls/rlp", version = "0.3", default-features = false, optional = true }
 impl-codec = { version = "0.6.0", path = "../primitive-types/impls/codec", default-features = false, optional = true }
 scale-info = { version = ">=1.0, <3", features = ["derive"], default-features = false, optional = true }
diff --git a/ethereum-types/Cargo.toml b/ethereum-types/Cargo.toml
index 3449bec..20f132b 100644
--- a/ethereum-types/Cargo.toml
+++ b/ethereum-types/Cargo.toml
@@ -6,14 +6,14 @@ license = "MIT OR Apache-2.0"
 homepage = "https://github.com/paritytech/parity-common"
 description = "Ethereum types"
 edition = "2021"
-rust-version = "1.56.1"
+rust-version = "1.60.0"
 
 [dependencies]
-ethbloom = { path = "../ethbloom", version = "0.12", default-features = false }
+ethbloom = { path = "../ethbloom", version = "0.12", optional = true, default-features = false }
 fixed-hash = { path = "../fixed-hash", version = "0.7", default-features = false, features = ["byteorder", "rustc-hex"] }
 uint-crate = { path = "../uint", package = "uint", version = "0.9", default-features = false }
 primitive-types = { path = "../primitive-types", version = "0.11", features = ["byteorder", "rustc-hex"], default-features = false }
-impl-serde = { path = "../primitive-types/impls/serde", version = "0.3.2", default-features = false, optional = true }
+impl-serde = { path = "../primitive-types/impls/serde", version = "0.4.0", default-features = false, optional = true }
 impl-rlp = { path = "../primitive-types/impls/rlp", version = "0.3", default-features = false, optional = true }
 impl-codec = { version = "0.6.0", path = "../primitive-types/impls/codec", default-features = false, optional = true }
 scale-info = { version = ">=1.0, <3", features = ["derive"], default-features = false, optional = true }
@@ -22,8 +22,8 @@ scale-info = { version = ">=1.0, <3", features = ["derive"], default-features =
 serde_json = "1.0.41"
 
 [features]
-default = ["std", "rlp", "serialize"]
-std = ["uint-crate/std", "fixed-hash/std", "ethbloom/std", "primitive-types/std"]
+default = ["std", "ethbloom", "rlp", "serialize"]
+std = ["uint-crate/std", "fixed-hash/std", "ethbloom?/std", "primitive-types/std"]
 serialize = ["impl-serde", "primitive-types/serde_no_std", "ethbloom/serialize"]
 arbitrary = ["ethbloom/arbitrary", "fixed-hash/arbitrary", "uint-crate/arbitrary"]
 rlp = ["impl-rlp", "ethbloom/rlp", "primitive-types/rlp"]
diff --git a/ethereum-types/src/lib.rs b/ethereum-types/src/lib.rs
index 56963f2..ee4dccc 100644
--- a/ethereum-types/src/lib.rs
+++ b/ethereum-types/src/lib.rs
@@ -11,6 +11,7 @@
 mod hash;
 mod uint;
 
+#[cfg(feature = "ethbloom")]
 pub use ethbloom::{Bloom, BloomRef, Input as BloomInput};
 pub use hash::{BigEndianHash, H128, H160, H256, H264, H32, H512, H520, H64};
 pub use uint::{FromDecStrErr, FromStrRadixErr, FromStrRadixErrKind, U128, U256, U512, U64};
diff --git a/fixed-hash/Cargo.toml b/fixed-hash/Cargo.toml
index 27ad7d2..d66dad7 100644
--- a/fixed-hash/Cargo.toml
+++ b/fixed-hash/Cargo.toml
@@ -16,7 +16,7 @@ features = ["quickcheck", "api-dummy"]
 
 [dependencies]
 byteorder = { version = "1.4.2", optional = true, default-features = false }
-quickcheck = { version = "0.9.0", optional = true }
+quickcheck = { version = "1", optional = true }
 rand = { version = "0.8.0", optional = true, default-features = false }
 rustc-hex = { version = "2.0.1", optional = true, default-features = false }
 static_assertions = "1.0.0"
diff --git a/fixed-hash/src/hash.rs b/fixed-hash/src/hash.rs
index 2322458..9dc356c 100644
--- a/fixed-hash/src/hash.rs
+++ b/fixed-hash/src/hash.rs
@@ -629,9 +629,8 @@ macro_rules! impl_quickcheck_for_fixed_hash {
 macro_rules! impl_quickcheck_for_fixed_hash {
 	( $name:ident ) => {
 		impl $crate::quickcheck::Arbitrary for $name {
-			fn arbitrary<G: $crate::quickcheck::Gen>(g: &mut G) -> Self {
-				let mut res = [0u8; $crate::core_::mem::size_of::<Self>()];
-				g.fill_bytes(&mut res[..Self::len_bytes()]);
+			fn arbitrary(g: &mut $crate::quickcheck::Gen) -> Self {
+				let res: [u8; Self::len_bytes()] = $crate::core_::array::from_fn(|_| u8::arbitrary(g));
 				Self::from(res)
 			}
 		}
diff --git a/kvdb-rocksdb/Cargo.toml b/kvdb-rocksdb/Cargo.toml
index 0b46fc2..675083b 100644
--- a/kvdb-rocksdb/Cargo.toml
+++ b/kvdb-rocksdb/Cargo.toml
@@ -41,6 +41,6 @@ kvdb-shared-tests = { path = "../kvdb-shared-tests", version = "0.9" }
 rand = "0.8.0"
 tempfile = "3.1.0"
 keccak-hash = { path = "../keccak-hash" }
-sysinfo = "0.25.1"
+sysinfo = "0.26.0"
 ctrlc = "3.1.4"
 chrono = "0.4"
diff --git a/kvdb-rocksdb/src/lib.rs b/kvdb-rocksdb/src/lib.rs
index 16de2a1..beb137f 100644
--- a/kvdb-rocksdb/src/lib.rs
+++ b/kvdb-rocksdb/src/lib.rs
@@ -12,14 +12,13 @@ mod stats;
 use std::{
 	cmp,
 	collections::HashMap,
-	error, fs, io,
+	error, io,
 	path::{Path, PathBuf},
-	result,
 };
 
 use parity_util_mem::MallocSizeOf;
 use rocksdb::{
-	BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, Error, Options, ReadOptions, WriteBatch, WriteOptions, DB,
+	BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, Options, ReadOptions, WriteBatch, WriteOptions, DB,
 };
 
 use kvdb::{DBKeyValue, DBOp, DBTransaction, DBValue, KeyValueDB};
@@ -319,24 +318,6 @@ pub struct Database {
 	stats: stats::RunningDbStats,
 }
 
-#[inline]
-fn check_for_corruption<T, P: AsRef<Path>>(path: P, res: result::Result<T, Error>) -> io::Result<T> {
-	if let Err(ref s) = res {
-		if is_corrupted(s) {
-			warn!("DB corrupted: {}. Repair will be triggered on next restart", s);
-			let _ = fs::File::create(path.as_ref().join(Database::CORRUPTION_FILE_NAME));
-		}
-	}
-
-	res.map_err(other_io_err)
-}
-
-fn is_corrupted(err: &Error) -> bool {
-	err.as_ref().starts_with("Corruption:") ||
-		err.as_ref()
-			.starts_with("Invalid argument: You have to open all column families")
-}
-
 /// Generate the options for RocksDB, based on the given `DatabaseConfig`.
 fn generate_options(config: &DatabaseConfig) -> Options {
 	let mut opts = Options::default();
@@ -395,8 +376,6 @@ fn generate_block_based_options(config: &DatabaseConfig) -> io::Result<BlockBase
 }
 
 impl Database {
-	const CORRUPTION_FILE_NAME: &'static str = "CORRUPTED";
-
 	/// Open database file.
 	///
 	/// # Safety
@@ -408,14 +387,6 @@ impl Database {
 		let opts = generate_options(config);
 		let block_opts = generate_block_based_options(config)?;
 
-		// attempt database repair if it has been previously marked as corrupted
-		let db_corrupted = path.as_ref().join(Database::CORRUPTION_FILE_NAME);
-		if db_corrupted.exists() {
-			warn!("DB has been previously marked as corrupted, attempting repair");
-			DB::repair(&opts, path.as_ref()).map_err(other_io_err)?;
-			fs::remove_file(db_corrupted)?;
-		}
-
 		let column_names: Vec<_> = (0..config.columns).map(|c| format!("col{}", c)).collect();
 		let write_opts = WriteOptions::default();
 		let read_opts = generate_read_options();
@@ -471,18 +442,6 @@ impl Database {
 
 		Ok(match db {
 			Ok(db) => db,
-			Err(ref s) if is_corrupted(s) => {
-				warn!("DB corrupted: {}, attempting repair", s);
-				DB::repair(&opts, path.as_ref()).map_err(other_io_err)?;
-
-				let cf_descriptors: Vec<_> = (0..config.columns)
-					.map(|i| {
-						ColumnFamilyDescriptor::new(column_names[i as usize], config.column_config(&block_opts, i))
-					})
-					.collect();
-
-				DB::open_cf_descriptors(&opts, path, cf_descriptors).map_err(other_io_err)?
-			},
 			Err(s) => return Err(other_io_err(s)),
 		})
 	}
@@ -499,11 +458,6 @@ impl Database {
 
 		Ok(match db {
 			Ok(db) => db,
-			Err(ref s) if is_corrupted(s) => {
-				warn!("DB corrupted: {}, attempting repair", s);
-				DB::repair(&opts, path.as_ref()).map_err(other_io_err)?;
-				DB::open_cf_as_secondary(&opts, path, secondary_path, column_names).map_err(other_io_err)?
-			},
 			Err(s) => return Err(other_io_err(s)),
 		})
 	}
@@ -555,7 +509,7 @@ impl Database {
 		}
 		self.stats.tally_bytes_written(stats_total_bytes as u64);
 
-		check_for_corruption(&self.path, cfs.db.write_opt(batch, &self.write_opts))
+		cfs.db.write_opt(batch, &self.write_opts).map_err(other_io_err)
 	}
 
 	/// Get value by key.
diff --git a/primitive-types/Cargo.toml b/primitive-types/Cargo.toml
index f65c105..5eb5ec0 100644
--- a/primitive-types/Cargo.toml
+++ b/primitive-types/Cargo.toml
@@ -11,7 +11,7 @@ rust-version = "1.60.0"
 [dependencies]
 fixed-hash = { version = "0.7", path = "../fixed-hash", default-features = false }
 uint = { version = "0.9.0", path = "../uint", default-features = false }
-impl-serde = { version = "0.3.1", path = "impls/serde", default-features = false, optional = true }
+impl-serde = { version = "0.4.0", path = "impls/serde", default-features = false, optional = true }
 impl-codec = { version = "0.6.0", path = "impls/codec", default-features = false, optional = true }
 impl-num-traits = { version = "0.1.0", path = "impls/num-traits", default-features = false, optional = true }
 impl-rlp = { version = "0.3", path = "impls/rlp", default-features = false, optional = true }
diff --git a/primitive-types/impls/serde/CHANGELOG.md b/primitive-types/impls/serde/CHANGELOG.md
index 5d143f9..0757363 100644
--- a/primitive-types/impls/serde/CHANGELOG.md
+++ b/primitive-types/impls/serde/CHANGELOG.md
@@ -4,7 +4,9 @@ The format is based on [Keep a Changelog].
 
 [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
 
-## [Unreleased]
+## [0.4.0] - 2022-09-02
+- Support deserializing H256 et al from bytes or sequences of bytes, too. [#668](https://github.com/paritytech/parity-common/pull/668)
+- Support deserializing H256 et al from newtype structs containing anything compatible, too. [#672](https://github.com/paritytech/parity-common/pull/672)
 - Migrated to 2021 edition, enforcing MSRV of `1.56.1`. [#601](https://github.com/paritytech/parity-common/pull/601)
 
 ## [0.3.2] - 2021-11-10
diff --git a/primitive-types/impls/serde/Cargo.toml b/primitive-types/impls/serde/Cargo.toml
index f94c754..91915ed 100644
--- a/primitive-types/impls/serde/Cargo.toml
+++ b/primitive-types/impls/serde/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "impl-serde"
-version = "0.3.2"
+version = "0.4.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 license = "MIT OR Apache-2.0"
 homepage = "https://github.com/paritytech/parity-common"
diff --git a/primitive-types/impls/serde/src/serialize.rs b/primitive-types/impls/serde/src/serialize.rs
index ee3a57c..3017170 100644
--- a/primitive-types/impls/serde/src/serialize.rs
+++ b/primitive-types/impls/serde/src/serialize.rs
@@ -187,8 +187,8 @@ pub enum ExpectedLen<'a> {
 impl<'a> fmt::Display for ExpectedLen<'a> {
 	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 		match *self {
-			ExpectedLen::Exact(ref v) => write!(fmt, "length of {}", v.len() * 2),
-			ExpectedLen::Between(min, ref v) => write!(fmt, "length between ({}; {}]", min * 2, v.len() * 2),
+			ExpectedLen::Exact(ref v) => write!(fmt, "{} bytes", v.len()),
+			ExpectedLen::Between(min, ref v) => write!(fmt, "between ({}; {}] bytes", min, v.len()),
 		}
 	}
 }
@@ -205,7 +205,7 @@ where
 		type Value = Vec<u8>;
 
 		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-			write!(formatter, "a (both 0x-prefixed or not) hex string")
+			write!(formatter, "a (both 0x-prefixed or not) hex string or byte array")
 		}
 
 		fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
@@ -215,6 +215,26 @@ where
 		fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
 			self.visit_str(&v)
 		}
+
+		fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
+			Ok(v.to_vec())
+		}
+
+		fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
+			Ok(v)
+		}
+
+		fn visit_seq<A: de::SeqAccess<'b>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
+			let mut bytes = vec![];
+			while let Some(n) = seq.next_element::<u8>()? {
+				bytes.push(n);
+			}
+			Ok(bytes)
+		}
+
+		fn visit_newtype_struct<D: Deserializer<'b>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
+			deserializer.deserialize_bytes(self)
+		}
 	}
 
 	deserializer.deserialize_str(Visitor)
@@ -234,7 +254,7 @@ where
 		type Value = usize;
 
 		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-			write!(formatter, "a (both 0x-prefixed or not) hex string with {}", self.len)
+			write!(formatter, "a (both 0x-prefixed or not) hex string or byte array containing {}", self.len)
 		}
 
 		fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
@@ -261,6 +281,42 @@ where
 		fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
 			self.visit_str(&v)
 		}
+
+		fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
+			let len = v.len();
+			let is_len_valid = match self.len {
+				ExpectedLen::Exact(ref slice) => len == slice.len(),
+				ExpectedLen::Between(min, ref slice) => len <= slice.len() && len > min,
+			};
+
+			if !is_len_valid {
+				return Err(E::invalid_length(v.len(), &self))
+			}
+
+			let bytes = match self.len {
+				ExpectedLen::Exact(slice) => slice,
+				ExpectedLen::Between(_, slice) => slice,
+			};
+
+			bytes[..len].copy_from_slice(v);
+			Ok(len)
+		}
+
+		fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
+			self.visit_bytes(&v)
+		}
+
+		fn visit_seq<A: de::SeqAccess<'b>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
+			let mut v = vec![];
+			while let Some(n) = seq.next_element::<u8>()? {
+				v.push(n);
+			}
+			self.visit_byte_buf(v)
+		}
+
+		fn visit_newtype_struct<D: Deserializer<'b>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
+			deserializer.deserialize_bytes(self)
+		}
 	}
 
 	deserializer.deserialize_str(Visitor { len })
@@ -367,4 +423,82 @@ mod tests {
 		assert_eq!(from_hex("102"), Ok(vec![1, 2]));
 		assert_eq!(from_hex("f"), Ok(vec![0xf]));
 	}
+
+	#[test]
+	fn should_deserialize_from_owned_bytes() {
+		type BytesDeserializer<'a> = serde::de::value::BytesDeserializer<'a, serde::de::value::Error>;
+
+		// using `deserialize` to decode owned bytes.
+		let des = BytesDeserializer::new(&[1, 2, 3, 4, 5]);
+		let deserialized: Vec<u8> = deserialize(des).unwrap();
+		assert_eq!(deserialized, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode owned bytes into buffer with fixed length.
+		let des = BytesDeserializer::new(&[1, 2, 3, 4, 5]);
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Exact(&mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 5);
+		assert_eq!(output, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode owned bytes into buffer with min/max length.
+		let des = BytesDeserializer::new(&[1, 2, 3]);
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Between(2, &mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 3);
+		assert_eq!(output, vec![1, 2, 3, 0, 0]);
+	}
+
+	#[test]
+	fn should_deserialize_from_borrowed_bytes() {
+		type BytesDeserializer<'a> = serde::de::value::BorrowedBytesDeserializer<'a, serde::de::value::Error>;
+
+		// using `deserialize` to decode borrowed bytes.
+		let des = BytesDeserializer::new(&[1, 2, 3, 4, 5]);
+		let deserialized: Vec<u8> = deserialize(des).unwrap();
+		assert_eq!(deserialized, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode borrowed bytes into buffer with fixed length.
+		let des = BytesDeserializer::new(&[1, 2, 3, 4, 5]);
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Exact(&mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 5);
+		assert_eq!(output, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode borrowed bytes into buffer with min/max length.
+		let des = BytesDeserializer::new(&[1, 2, 3]);
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Between(2, &mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 3);
+		assert_eq!(output, vec![1, 2, 3, 0, 0]);
+	}
+
+	#[test]
+	fn should_deserialize_from_u8_sequence() {
+		use serde::de::value::SeqDeserializer;
+
+		// using `deserialize` to decode a sequence of bytes.
+		let des = SeqDeserializer::<_, serde::de::value::Error>::new([1u8, 2, 3, 4, 5].into_iter());
+		let deserialized: Vec<u8> = deserialize(des).unwrap();
+		assert_eq!(deserialized, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode a sequence of bytes into a buffer with fixed length.
+		let des = SeqDeserializer::<_, serde::de::value::Error>::new([1u8, 2, 3, 4, 5].into_iter());
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Exact(&mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 5);
+		assert_eq!(output, vec![1, 2, 3, 4, 5]);
+
+		// using `deserialize` to decode a sequence of bytes into a buffer with min/max length.
+		let des = SeqDeserializer::<_, serde::de::value::Error>::new([1u8, 2, 3].into_iter());
+		let mut output = vec![0, 0, 0, 0, 0];
+		let expected_len = ExpectedLen::Between(2, &mut *output);
+		let n = deserialize_check_len(des, expected_len).unwrap();
+		assert_eq!(n, 3);
+		assert_eq!(output, vec![1, 2, 3, 0, 0]);
+	}
 }
diff --git a/uint/Cargo.toml b/uint/Cargo.toml
index 1de346d..7be3e29 100644
--- a/uint/Cargo.toml
+++ b/uint/Cargo.toml
@@ -13,8 +13,7 @@ rust-version = "1.56.1"
 [dependencies]
 byteorder = { version = "1.4.2", default-features = false }
 crunchy = { version = "0.2.2", default-features = false }
-qc = { package = "quickcheck", version = "0.9.0", optional = true }
-rand07 = { package = "rand", version = "0.7", default-features = false, optional = true }
+quickcheck = { version = "1", optional = true }
 hex = { version = "0.4", default-features = false }
 static_assertions = "1.0.0"
 arbitrary = { version = "1.0", optional = true }
@@ -22,7 +21,6 @@ arbitrary = { version = "1.0", optional = true }
 [features]
 default = ["std"]
 std = ["byteorder/std", "crunchy/std", "hex/std"]
-quickcheck = ["qc", "rand07"]
 
 [[example]]
 name = "modular"
diff --git a/uint/src/lib.rs b/uint/src/lib.rs
index 83ab957..e259c79 100644
--- a/uint/src/lib.rs
+++ b/uint/src/lib.rs
@@ -23,11 +23,7 @@ pub use hex;
 
 #[cfg(feature = "quickcheck")]
 #[doc(hidden)]
-pub use qc;
-
-#[cfg(feature = "quickcheck")]
-#[doc(hidden)]
-pub use rand07;
+pub use quickcheck;
 
 #[cfg(feature = "arbitrary")]
 #[doc(hidden)]
diff --git a/uint/src/uint.rs b/uint/src/uint.rs
index 1b633fc..728957d 100644
--- a/uint/src/uint.rs
+++ b/uint/src/uint.rs
@@ -1741,28 +1741,33 @@ macro_rules! construct_uint {
 #[doc(hidden)]
 macro_rules! impl_quickcheck_arbitrary_for_uint {
 	($uint: ty, $n_bytes: tt) => {
-		impl $crate::qc::Arbitrary for $uint {
-			fn arbitrary<G: $crate::qc::Gen>(g: &mut G) -> Self {
-				let mut res = [0u8; $n_bytes];
-
-				use $crate::rand07::Rng;
-				let p: f64 = $crate::rand07::rngs::OsRng.gen();
+		impl $crate::quickcheck::Arbitrary for $uint {
+			fn arbitrary(g: &mut $crate::quickcheck::Gen) -> Self {
+				let p = usize::arbitrary(g) % 100;
 				// make it more likely to generate smaller numbers that
 				// don't use up the full $n_bytes
 				let range =
 					// 10% chance to generate number that uses up to $n_bytes
-					if p < 0.1 {
+					if p < 10 {
 						$n_bytes
 					// 10% chance to generate number that uses up to $n_bytes / 2
-					} else if p < 0.2 {
+					} else if p < 20 {
 						$n_bytes / 2
 					// 80% chance to generate number that uses up to $n_bytes / 5
 					} else {
 						$n_bytes / 5
 					};
 
-				let size = g.gen_range(0, range);
-				g.fill_bytes(&mut res[..size]);
+				let range = $crate::core_::cmp::max(range, 1);
+				let size: usize = usize::arbitrary(g) % range;
+
+				let res: [u8; $n_bytes] = $crate::core_::array::from_fn(|i| {
+					if i > size {
+						0
+					} else {
+						u8::arbitrary(g)
+					}
+				});
 
 				res.as_ref().into()
 			}


Test patch:
diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs
index f378423..9ef7f2e 100644
--- a/uint/tests/uint_tests.rs
+++ b/uint/tests/uint_tests.rs
@@ -1157,7 +1157,7 @@ pub mod laws {
 	macro_rules! uint_laws {
 		($mod_name:ident, $uint_ty:ident) => {
 			mod $mod_name {
-				use qc::{TestResult, quickcheck};
+				use quickcheck::{TestResult, quickcheck};
 				use super::$uint_ty;
 
 				quickcheck! {