diff --git a/Cargo.lock b/Cargo.lock index 6122a592fe..0591657624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,6 +719,19 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ckb-gen-types" +version = "0.112.0-pre" +dependencies = [ + "cfg-if 1.0.0", + "ckb-error", + "ckb-fixed-hash", + "ckb-hash", + "ckb-occupied-capacity", + "molecule", + "numext-fixed-uint", +] + [[package]] name = "ckb-hash" version = "0.112.0-pre" @@ -1446,6 +1459,7 @@ dependencies = [ "ckb-constant", "ckb-error", "ckb-fixed-hash", + "ckb-gen-types", "ckb-hash", "ckb-merkle-mountain-range", "ckb-occupied-capacity", diff --git a/Cargo.toml b/Cargo.toml index 4d0c0cd5fe..6b3d0278a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ members = [ "util/constant", "error", "util/multisig", + "util/gen-types", "util/types", "util/jsonrpc-types", "freezer", diff --git a/Makefile b/Makefile index 7d7adb98fb..9559c39912 100644 --- a/Makefile +++ b/Makefile @@ -224,8 +224,8 @@ check-dirty-hashes-toml: gen-hashes ##@ Generates Files .PHONY: gen -GEN_MOL_IN_DIR := util/types/schemas -GEN_MOL_OUT_DIR := util/types/src/generated +GEN_MOL_IN_DIR := util/gen-types/schemas +GEN_MOL_OUT_DIR := util/gen-types/src/generated GEN_MOL_FILES := ${GEN_MOL_OUT_DIR}/blockchain.rs ${GEN_MOL_OUT_DIR}/extensions.rs ${GEN_MOL_OUT_DIR}/protocols.rs gen: check-moleculec-version ${GEN_MOL_FILES} # Generate Protocol Files diff --git a/sync/src/relayer/block_transactions_verifier.rs b/sync/src/relayer/block_transactions_verifier.rs index ec9c3a87b8..de0d3a0667 100644 --- a/sync/src/relayer/block_transactions_verifier.rs +++ b/sync/src/relayer/block_transactions_verifier.rs @@ -1,5 +1,5 @@ use crate::{Status, StatusCode}; -use ckb_types::{core, packed}; +use ckb_types::{core, packed, prelude::*}; pub struct BlockTransactionsVerifier {} diff --git a/test/src/specs/sync/sync_and_mine.rs b/test/src/specs/sync/sync_and_mine.rs index c78713729d..77e86f2d17 100644 --- a/test/src/specs/sync/sync_and_mine.rs +++ b/test/src/specs/sync/sync_and_mine.rs @@ -1,5 +1,5 @@ use crate::{Node, Spec}; -use ckb_types::packed; +use ckb_types::{packed, prelude::*}; use std::thread::sleep; use std::time::{Duration, Instant}; diff --git a/util/gen-types/Cargo.toml b/util/gen-types/Cargo.toml new file mode 100644 index 0000000000..f37051e3c7 --- /dev/null +++ b/util/gen-types/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "ckb-gen-types" +version = "0.112.0-pre" +authors = ["Nervos Core Dev "] +edition = "2021" +license = "MIT" +description = "Provides the generated types for CKB." +homepage = "https://github.com/nervosnetwork/ckb" +repository = "https://github.com/nervosnetwork/ckb" + +[dev-dependencies] +ckb-hash = {path = "../hash", version = "= 0.112.0-pre"} + +[features] +default = ["std"] +# Enable the `calc-hash` extension for CKB contract development in `no-std` env +calc-hash = ["ckb-hash/ckb-contract"] +# Enable the `check-data` extension for CKB contract development in `no-std` env +check-data = [] +# Enable the `serialized-size` extension for CKB contract development in `no-std` env +serialized-size = ["calc-hash"] +# Enable all in `std` env +std = ["molecule/std", "ckb-fixed-hash", "ckb-error", "ckb-occupied-capacity", "numext-fixed-uint"] + +[dependencies] +cfg-if = "1.0" +ckb-hash = { path = "../hash", version = "= 0.112.0-pre" } +molecule = { version = "0.7.5", default-features = false } +ckb-fixed-hash = { path = "../fixed-hash", version = "= 0.112.0-pre", optional = true } +ckb-error = { path = "../../error", version = "= 0.112.0-pre", optional = true } +ckb-occupied-capacity = { path = "../occupied-capacity", version = "= 0.112.0-pre", optional = true } +numext-fixed-uint = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"], optional = true } diff --git a/util/types/schemas/blockchain.mol b/util/gen-types/schemas/blockchain.mol similarity index 100% rename from util/types/schemas/blockchain.mol rename to util/gen-types/schemas/blockchain.mol diff --git a/util/types/schemas/extensions.mol b/util/gen-types/schemas/extensions.mol similarity index 100% rename from util/types/schemas/extensions.mol rename to util/gen-types/schemas/extensions.mol diff --git a/util/types/schemas/protocols.mol b/util/gen-types/schemas/protocols.mol similarity index 100% rename from util/types/schemas/protocols.mol rename to util/gen-types/schemas/protocols.mol diff --git a/util/gen-types/src/conversion/blockchain/mod.rs b/util/gen-types/src/conversion/blockchain/mod.rs new file mode 100644 index 0000000000..6c9dd00632 --- /dev/null +++ b/util/gen-types/src/conversion/blockchain/mod.rs @@ -0,0 +1,54 @@ +#[cfg(feature = "std")] +mod std_env; + +use crate::{borrow::ToOwned, bytes::Bytes, generated::packed, prelude::*, vec::Vec}; + +impl Pack for [u8; 32] { + fn pack(&self) -> packed::Byte32 { + packed::Byte32::from_slice(&self[..]).expect("impossible: fail to pack [u8; 32]") + } +} + +impl Pack for [u8; 10] { + fn pack(&self) -> packed::ProposalShortId { + packed::ProposalShortId::from_slice(&self[..]) + .expect("impossible: fail to pack to ProposalShortId") + } +} + +impl Pack for Bytes { + fn pack(&self) -> packed::Bytes { + let len = (self.len() as u32).to_le_bytes(); + let mut v = Vec::with_capacity(4 + self.len()); + v.extend_from_slice(&len[..]); + v.extend_from_slice(&self[..]); + packed::Bytes::new_unchecked(v.into()) + } +} + +impl<'r> Unpack for packed::BytesReader<'r> { + fn unpack(&self) -> Bytes { + Bytes::from(self.raw_data().to_owned()) + } +} + +impl Unpack for packed::Bytes { + fn unpack(&self) -> Bytes { + self.raw_data() + } +} + +impl_conversion_for_vector!(Bytes, BytesVec, BytesVecReader); +impl_conversion_for_packed_optional_pack!(Byte32, Byte32Opt); +impl_conversion_for_packed_optional_pack!(CellOutput, CellOutputOpt); +impl_conversion_for_packed_optional_pack!(Script, ScriptOpt); +impl_conversion_for_packed_iterator_pack!(ProposalShortId, ProposalShortIdVec); +impl_conversion_for_packed_iterator_pack!(Bytes, BytesVec); +impl_conversion_for_packed_iterator_pack!(Transaction, TransactionVec); +impl_conversion_for_packed_iterator_pack!(OutPoint, OutPointVec); +impl_conversion_for_packed_iterator_pack!(CellDep, CellDepVec); +impl_conversion_for_packed_iterator_pack!(CellOutput, CellOutputVec); +impl_conversion_for_packed_iterator_pack!(CellInput, CellInputVec); +impl_conversion_for_packed_iterator_pack!(UncleBlock, UncleBlockVec); +impl_conversion_for_packed_iterator_pack!(Header, HeaderVec); +impl_conversion_for_packed_iterator_pack!(Byte32, Byte32Vec); diff --git a/util/gen-types/src/conversion/blockchain/std_env.rs b/util/gen-types/src/conversion/blockchain/std_env.rs new file mode 100644 index 0000000000..5fd0f40751 --- /dev/null +++ b/util/gen-types/src/conversion/blockchain/std_env.rs @@ -0,0 +1,47 @@ +use ckb_fixed_hash::H256; +use ckb_occupied_capacity::Capacity; +use numext_fixed_uint::U256; + +use crate::{packed, prelude::*}; + +impl Pack for Capacity { + fn pack(&self) -> packed::Uint64 { + self.as_u64().pack() + } +} + +impl<'r> Unpack for packed::Uint64Reader<'r> { + fn unpack(&self) -> Capacity { + Capacity::shannons(self.unpack()) + } +} +impl_conversion_for_entity_unpack!(Capacity, Uint64); + +impl Pack for U256 { + fn pack(&self) -> packed::Uint256 { + packed::Uint256::from_slice(&self.to_le_bytes()[..]).expect("impossible: fail to pack U256") + } +} + +impl<'r> Unpack for packed::Uint256Reader<'r> { + fn unpack(&self) -> U256 { + U256::from_little_endian(self.as_slice()).expect("internal error: fail to unpack U256") + } +} +impl_conversion_for_entity_unpack!(U256, Uint256); + +impl Pack for H256 { + fn pack(&self) -> packed::Byte32 { + packed::Byte32::from_slice(self.as_bytes()).expect("impossible: fail to pack H256") + } +} + +impl<'r> Unpack for packed::Byte32Reader<'r> { + fn unpack(&self) -> H256 { + H256::from_slice(self.as_slice()).expect("internal error: fail to unpack H256") + } +} +impl_conversion_for_entity_unpack!(H256, Byte32); + +impl_conversion_for_option!(H256, Byte32Opt, Byte32OptReader); +impl_conversion_for_vector!(Capacity, Uint64Vec, Uint64VecReader); diff --git a/util/gen-types/src/conversion/mod.rs b/util/gen-types/src/conversion/mod.rs new file mode 100644 index 0000000000..a6daf429ee --- /dev/null +++ b/util/gen-types/src/conversion/mod.rs @@ -0,0 +1,6 @@ +#[macro_use] +mod utilities; + +mod blockchain; +mod network; +mod primitive; diff --git a/util/types/src/conversion/network.rs b/util/gen-types/src/conversion/network.rs similarity index 100% rename from util/types/src/conversion/network.rs rename to util/gen-types/src/conversion/network.rs diff --git a/util/types/src/conversion/primitive.rs b/util/gen-types/src/conversion/primitive.rs similarity index 94% rename from util/types/src/conversion/primitive.rs rename to util/gen-types/src/conversion/primitive.rs index 69357eab47..38d82d9228 100644 --- a/util/types/src/conversion/primitive.rs +++ b/util/gen-types/src/conversion/primitive.rs @@ -1,4 +1,9 @@ -use crate::{bytes::Bytes, packed, prelude::*}; +#[cfg(not(feature = "std"))] +use alloc::{borrow::ToOwned, str, string::String}; +#[cfg(feature = "std")] +use std::str; + +use crate::{bytes::Bytes, generated::packed, prelude::*, vec, vec::Vec}; impl Pack for bool { fn pack(&self) -> packed::Bool { @@ -146,8 +151,8 @@ impl Pack for str { impl<'r> packed::BytesReader<'r> { /// Converts self to a string slice. - pub fn as_utf8(&self) -> Result<&str, ::std::str::Utf8Error> { - ::std::str::from_utf8(self.raw_data()) + pub fn as_utf8(&self) -> Result<&str, str::Utf8Error> { + str::from_utf8(self.raw_data()) } /// Converts self to a string slice without checking that the string contains valid UTF-8. @@ -158,7 +163,7 @@ impl<'r> packed::BytesReader<'r> { /// it are valid UTF-8. If this constraint is violated, undefined behavior /// results, as the rest of Rust assumes that [`&str`]s are valid UTF-8. pub unsafe fn as_utf8_unchecked(&self) -> &str { - ::std::str::from_utf8_unchecked(self.raw_data()) + str::from_utf8_unchecked(self.raw_data()) } /// Checks whether self is contains valid UTF-8 binary data. diff --git a/util/gen-types/src/conversion/utilities.rs b/util/gen-types/src/conversion/utilities.rs new file mode 100644 index 0000000000..cd5faa9a78 --- /dev/null +++ b/util/gen-types/src/conversion/utilities.rs @@ -0,0 +1,98 @@ +macro_rules! impl_conversion_for_entity_unpack { + ($original:ty, $entity:ident) => { + impl Unpack<$original> for packed::$entity { + fn unpack(&self) -> $original { + self.as_reader().unpack() + } + } + }; +} + +macro_rules! impl_conversion_for_option_pack { + ($original:ty, $entity:ident) => { + impl Pack for Option<$original> { + fn pack(&self) -> packed::$entity { + if let Some(ref inner) = self { + packed::$entity::new_unchecked(inner.pack().as_bytes()) + } else { + packed::$entity::default() + } + } + } + }; +} + +macro_rules! impl_conversion_for_option_unpack { + ($original:ty, $entity:ident, $reader:ident) => { + impl<'r> Unpack> for packed::$reader<'r> { + fn unpack(&self) -> Option<$original> { + self.to_opt().map(|x| x.unpack()) + } + } + impl_conversion_for_entity_unpack!(Option<$original>, $entity); + }; +} + +macro_rules! impl_conversion_for_option { + ($original:ty, $entity:ident, $reader:ident) => { + impl_conversion_for_option_pack!($original, $entity); + impl_conversion_for_option_unpack!($original, $entity, $reader); + }; +} + +macro_rules! impl_conversion_for_vector_pack { + ($original:ty, $entity:ident) => { + impl Pack for [$original] { + fn pack(&self) -> packed::$entity { + packed::$entity::new_builder() + .set(self.iter().map(|v| v.pack()).collect()) + .build() + } + } + }; +} + +macro_rules! impl_conversion_for_vector_unpack { + ($original:ty, $entity:ident, $reader:ident) => { + impl<'r> Unpack> for packed::$reader<'r> { + fn unpack(&self) -> Vec<$original> { + self.iter().map(|x| x.unpack()).collect() + } + } + impl_conversion_for_entity_unpack!(Vec<$original>, $entity); + }; +} + +macro_rules! impl_conversion_for_vector { + ($original:ty, $entity:ident, $reader:ident) => { + impl_conversion_for_vector_pack!($original, $entity); + impl_conversion_for_vector_unpack!($original, $entity, $reader); + }; +} + +macro_rules! impl_conversion_for_packed_optional_pack { + ($original:ident, $entity:ident) => { + impl Pack for Option { + fn pack(&self) -> packed::$entity { + if let Some(ref inner) = self { + packed::$entity::new_unchecked(inner.as_bytes()) + } else { + packed::$entity::default() + } + } + } + }; +} + +macro_rules! impl_conversion_for_packed_iterator_pack { + ($item:ident, $vec:ident) => { + impl PackVec for T + where + T: IntoIterator, + { + fn pack(self) -> packed::$vec { + packed::$vec::new_builder().extend(self).build() + } + } + }; +} diff --git a/util/gen-types/src/core.rs b/util/gen-types/src/core.rs new file mode 100644 index 0000000000..0f688c2250 --- /dev/null +++ b/util/gen-types/src/core.rs @@ -0,0 +1,83 @@ +//! The essential rust types for CKB contracts. + +#![allow(clippy::from_over_into)] + +use crate::packed; + +/// Specifies how the script `code_hash` is used to match the script code and how to run the code. +/// The hash type is split into the high 7 bits and the low 1 bit, +/// when the low 1 bit is 1, it indicates the type, +/// when the low 1 bit is 0, it indicates the data, +/// and then it relies on the high 7 bits to indicate +/// that the data actually corresponds to the version. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum ScriptHashType { + /// Type "data" matches script code via cell data hash, and run the script code in v0 CKB VM. + Data = 0, + /// Type "type" matches script code via cell type script hash. + Type = 1, + /// Type "data1" matches script code via cell data hash, and run the script code in v1 CKB VM. + Data1 = 2, + /// Type "data2" matches script code via cell data hash, and run the script code in v2 CKB VM. + Data2 = 4, +} + +impl Default for ScriptHashType { + fn default() -> Self { + ScriptHashType::Data + } +} + +impl ScriptHashType { + #[inline] + pub(crate) fn verify_value(v: u8) -> bool { + v <= 4 && v != 3 + } +} + +impl Into for ScriptHashType { + #[inline] + fn into(self) -> u8 { + match self { + Self::Data => 0, + Self::Type => 1, + Self::Data1 => 2, + Self::Data2 => 4, + } + } +} + +impl Into for ScriptHashType { + #[inline] + fn into(self) -> packed::Byte { + Into::::into(self).into() + } +} + +#[cfg(feature = "std")] +mod std_mod { + use crate::{core::ScriptHashType, packed}; + use ckb_error::OtherError; + + impl TryFrom for ScriptHashType { + type Error = OtherError; + + fn try_from(v: u8) -> Result { + match v { + 0 => Ok(ScriptHashType::Data), + 1 => Ok(ScriptHashType::Type), + 2 => Ok(ScriptHashType::Data1), + 4 => Ok(ScriptHashType::Data2), + _ => Err(OtherError::new(format!("Invalid script hash type {v}"))), + } + } + } + + impl TryFrom for ScriptHashType { + type Error = OtherError; + + fn try_from(v: packed::Byte) -> Result { + Into::::into(v).try_into() + } + } +} diff --git a/util/types/src/extension/calc_hash.rs b/util/gen-types/src/extension/calc_hash.rs similarity index 94% rename from util/types/src/extension/calc_hash.rs rename to util/gen-types/src/extension/calc_hash.rs index 8af8318f3f..b552ca7e20 100644 --- a/util/types/src/extension/calc_hash.rs +++ b/util/gen-types/src/extension/calc_hash.rs @@ -1,6 +1,6 @@ use ckb_hash::{blake2b_256, new_blake2b}; -use crate::{core, packed, prelude::*}; +use crate::{packed, prelude::*, vec::Vec}; /* * Calculate simple hash for packed bytes wrappers. @@ -254,16 +254,6 @@ impl<'r> packed::BlockReader<'r> { .map(|extension| extension.calc_raw_data_hash()) } - /// Calculates the extra hash, which is a combination of the uncles hash and - /// the extension hash. - /// - /// - If there is no extension, extra hash is the same as the uncles hash. - /// - If there is a extension, then extra hash it the hash of the combination - /// of uncles hash and the extension hash. - pub fn calc_extra_hash(&self) -> core::ExtraHashView { - core::ExtraHashView::new(self.calc_uncles_hash(), self.calc_extension_hash()) - } - /// Calculates transaction hashes for all transactions in the block. pub fn calc_tx_hashes(&self) -> Vec { self.transactions() @@ -285,7 +275,6 @@ impl_calc_special_hash_for_entity!(Block, calc_header_hash); impl_calc_special_hash_for_entity!(Block, calc_proposals_hash); impl_calc_special_hash_for_entity!(Block, calc_uncles_hash); impl_calc_special_hash_for_entity!(Block, calc_extension_hash, Option); -impl_calc_special_hash_for_entity!(Block, calc_extra_hash, core::ExtraHashView); impl_calc_special_hash_for_entity!(Block, calc_tx_hashes, Vec); impl_calc_special_hash_for_entity!(Block, calc_tx_witness_hashes, Vec); diff --git a/util/types/src/extension/capacity.rs b/util/gen-types/src/extension/capacity.rs similarity index 97% rename from util/types/src/extension/capacity.rs rename to util/gen-types/src/extension/capacity.rs index 0c80129be7..ac4478378f 100644 --- a/util/types/src/extension/capacity.rs +++ b/util/gen-types/src/extension/capacity.rs @@ -1,6 +1,6 @@ -use ckb_occupied_capacity::Result as CapacityResult; +use ckb_occupied_capacity::{Capacity, Result as CapacityResult}; -use crate::{core::Capacity, packed, prelude::*}; +use crate::{packed, prelude::*}; impl packed::Script { /// Calculates the occupied capacity of [`Script`]. diff --git a/util/types/src/extension/check_data.rs b/util/gen-types/src/extension/check_data.rs similarity index 96% rename from util/types/src/extension/check_data.rs rename to util/gen-types/src/extension/check_data.rs index 04e69ec706..d476276562 100644 --- a/util/types/src/extension/check_data.rs +++ b/util/gen-types/src/extension/check_data.rs @@ -4,6 +4,8 @@ use crate::{core, packed}; * Blockchain */ +const MAX_DEP_TYPE: u8 = 1; + impl<'r> packed::ScriptReader<'r> { fn check_data(&self) -> bool { core::ScriptHashType::verify_value(self.hash_type().into()) @@ -32,7 +34,7 @@ impl<'r> packed::CellOutputVecReader<'r> { impl<'r> packed::CellDepReader<'r> { fn check_data(&self) -> bool { - core::DepType::verify_value(self.dep_type().into()) + MAX_DEP_TYPE >= self.dep_type().into() } } diff --git a/util/gen-types/src/extension/mod.rs b/util/gen-types/src/extension/mod.rs new file mode 100644 index 0000000000..eb1bc62cab --- /dev/null +++ b/util/gen-types/src/extension/mod.rs @@ -0,0 +1,17 @@ +mod rust_core_traits; + +#[cfg(any(feature = "calc-hash", feature = "std"))] +mod calc_hash; +#[cfg(any(feature = "calc-hash", feature = "std"))] +mod shortcut; + +#[cfg(any(feature = "check-data", feature = "std"))] +mod check_data; +#[cfg(any(feature = "serialized-size", feature = "std"))] +mod serialized_size; + +#[cfg(feature = "std")] +mod capacity; + +#[cfg(test)] +mod tests; diff --git a/util/gen-types/src/extension/rust_core_traits.rs b/util/gen-types/src/extension/rust_core_traits.rs new file mode 100644 index 0000000000..792895cf93 --- /dev/null +++ b/util/gen-types/src/extension/rust_core_traits.rs @@ -0,0 +1,222 @@ +use crate::{packed, prelude::*}; + +macro_rules! impl_cmp_eq_and_hash { + ($struct:ident) => { + impl ::core::cmp::PartialEq for packed::$struct { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_slice() == other.as_slice() + } + } + impl ::core::cmp::Eq for packed::$struct {} + + impl ::core::hash::Hash for packed::$struct { + #[inline] + fn hash(&self, state: &mut H) { + state.write(self.as_slice()) + } + } + }; +} + +impl_cmp_eq_and_hash!(Uint32); +impl_cmp_eq_and_hash!(Uint64); +impl_cmp_eq_and_hash!(Uint128); +impl_cmp_eq_and_hash!(Uint256); +impl_cmp_eq_and_hash!(Byte32); +impl_cmp_eq_and_hash!(Bytes); +impl_cmp_eq_and_hash!(BytesOpt); +impl_cmp_eq_and_hash!(ProposalShortId); +impl_cmp_eq_and_hash!(Script); +impl_cmp_eq_and_hash!(ScriptOpt); +impl_cmp_eq_and_hash!(CellDep); +impl_cmp_eq_and_hash!(OutPoint); +impl_cmp_eq_and_hash!(CellInput); +impl_cmp_eq_and_hash!(CellOutput); +impl_cmp_eq_and_hash!(Alert); +impl_cmp_eq_and_hash!(UncleBlock); +impl_cmp_eq_and_hash!(Block); +impl_cmp_eq_and_hash!(HeaderDigest); + +macro_rules! impl_cmp_partial_ord { + ($struct:ident) => { + impl ::core::cmp::PartialOrd for packed::$struct { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { + Some(self.cmp(other)) + } + } + }; +} + +impl ::core::cmp::Ord for packed::Uint32 { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let self_val: u32 = self.unpack(); + let other_val: u32 = other.unpack(); + self_val.cmp(&other_val) + } +} +impl_cmp_partial_ord!(Uint32); + +impl ::core::cmp::Ord for packed::Uint64 { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let self_val: u64 = self.unpack(); + let other_val: u64 = other.unpack(); + self_val.cmp(&other_val) + } +} +impl_cmp_partial_ord!(Uint64); + +impl ::core::cmp::Ord for packed::Uint128 { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let self_val: u128 = self.unpack(); + let other_val: u128 = other.unpack(); + self_val.cmp(&other_val) + } +} +impl_cmp_partial_ord!(Uint128); + +#[cfg(feature = "std")] +mod std_feature_mod { + use crate::{packed, prelude::*}; + use numext_fixed_uint::U256; + + impl ::core::cmp::Ord for packed::Uint256 { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let self_val: U256 = self.unpack(); + let other_val: U256 = other.unpack(); + self_val.cmp(&other_val) + } + } + impl_cmp_partial_ord!(Uint256); +} + +impl ::core::cmp::Ord for packed::Byte32 { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} +impl_cmp_partial_ord!(Byte32); + +impl ::core::cmp::Ord for packed::Bytes { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} +impl_cmp_partial_ord!(Bytes); + +impl ::core::cmp::Ord for packed::BytesOpt { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + match (self.to_opt(), other.to_opt()) { + (Some(bytes1), Some(bytes2)) => bytes1.cmp(&bytes2), + (Some(_), None) => ::core::cmp::Ordering::Greater, + (None, Some(_)) => ::core::cmp::Ordering::Less, + (None, None) => ::core::cmp::Ordering::Equal, + } + } +} +impl_cmp_partial_ord!(BytesOpt); + +impl ::core::cmp::Ord for packed::ProposalShortId { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} +impl_cmp_partial_ord!(ProposalShortId); + +impl ::core::cmp::Ord for packed::Script { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let code_hash_order = self.code_hash().cmp(&other.code_hash()); + if code_hash_order != ::core::cmp::Ordering::Equal { + return code_hash_order; + } + + let hash_type_order = self.hash_type().cmp(&other.hash_type()); + if hash_type_order != ::core::cmp::Ordering::Equal { + return hash_type_order; + } + + self.args().cmp(&other.args()) + } +} +impl_cmp_partial_ord!(Script); + +impl ::core::cmp::Ord for packed::ScriptOpt { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + match (self.to_opt(), other.to_opt()) { + (Some(script1), Some(script2)) => script1.cmp(&script2), + (Some(_), None) => ::core::cmp::Ordering::Greater, + (None, Some(_)) => ::core::cmp::Ordering::Less, + (None, None) => ::core::cmp::Ordering::Equal, + } + } +} +impl_cmp_partial_ord!(ScriptOpt); + +impl ::core::cmp::Ord for packed::CellDep { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let dep_type_order = self.dep_type().cmp(&other.dep_type()); + if dep_type_order != ::core::cmp::Ordering::Equal { + return dep_type_order; + } + + self.out_point().cmp(&other.out_point()) + } +} +impl_cmp_partial_ord!(CellDep); + +impl ::core::cmp::Ord for packed::OutPoint { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let tx_hash_order = self.tx_hash().cmp(&other.tx_hash()); + if tx_hash_order != ::core::cmp::Ordering::Equal { + return tx_hash_order; + } + + self.index().cmp(&other.index()) + } +} +impl_cmp_partial_ord!(OutPoint); + +impl ::core::cmp::Ord for packed::CellInput { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let previous_output_order = self.previous_output().cmp(&other.previous_output()); + if previous_output_order != ::core::cmp::Ordering::Equal { + return previous_output_order; + } + + // smaller since values are prioritized and appear earlier in the ordering + other.since().cmp(&self.since()) + } +} +impl_cmp_partial_ord!(CellInput); + +impl ::core::cmp::Ord for packed::CellOutput { + #[inline] + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + let lock_order = self.lock().cmp(&other.lock()); + if lock_order != ::core::cmp::Ordering::Equal { + return lock_order; + } + + let capacity_order = self.capacity().cmp(&other.capacity()); + if capacity_order != ::core::cmp::Ordering::Equal { + return capacity_order; + } + + self.type_().cmp(&other.type_()) + } +} +impl_cmp_partial_ord!(CellOutput); diff --git a/util/types/src/extension/serialized_size.rs b/util/gen-types/src/extension/serialized_size.rs similarity index 100% rename from util/types/src/extension/serialized_size.rs rename to util/gen-types/src/extension/serialized_size.rs diff --git a/util/types/src/extension/shortcuts.rs b/util/gen-types/src/extension/shortcut.rs similarity index 63% rename from util/types/src/extension/shortcuts.rs rename to util/gen-types/src/extension/shortcut.rs index 997e542705..84bfe9e0af 100644 --- a/util/types/src/extension/shortcuts.rs +++ b/util/gen-types/src/extension/shortcut.rs @@ -1,13 +1,6 @@ -use std::collections::HashSet; +use crate::{bytes, core, generated::packed, prelude::*, vec::Vec}; -use crate::{ - bytes, - core::{self, BlockNumber}, - packed, - prelude::*, - utilities::{compact_to_difficulty, merkle_root}, - U256, -}; +type BlockNumber = u64; impl packed::Byte32 { /// Creates a new `Bytes32` whose bits are all zeros. @@ -154,20 +147,6 @@ impl packed::Transaction { } } -impl packed::RawHeader { - /// Calculates the difficulty from compact target. - pub fn difficulty(&self) -> U256 { - compact_to_difficulty(self.compact_target().unpack()) - } -} - -impl packed::Header { - /// Calculates the difficulty from compact target. - pub fn difficulty(&self) -> U256 { - self.raw().difficulty() - } -} - impl packed::Block { /// Converts self to an uncle block. pub fn as_uncle(&self) -> packed::UncleBlock { @@ -177,46 +156,6 @@ impl packed::Block { .build() } - /// Recalculates all hashes and merkle roots in the header. - pub fn reset_header(self) -> packed::Block { - let tx_hashes = self.as_reader().calc_tx_hashes(); - let tx_witness_hashes = self.as_reader().calc_tx_witness_hashes(); - self.reset_header_with_hashes(&tx_hashes[..], &tx_witness_hashes[..]) - } - - pub(crate) fn reset_header_with_hashes( - self, - tx_hashes: &[packed::Byte32], - tx_witness_hashes: &[packed::Byte32], - ) -> packed::Block { - let raw_transactions_root = merkle_root(tx_hashes); - let witnesses_root = merkle_root(tx_witness_hashes); - let transactions_root = merkle_root(&[raw_transactions_root, witnesses_root]); - let proposals_hash = self.as_reader().calc_proposals_hash(); - let extra_hash = self.as_reader().calc_extra_hash().extra_hash(); - let raw_header = self - .header() - .raw() - .as_builder() - .transactions_root(transactions_root) - .proposals_hash(proposals_hash) - .extra_hash(extra_hash) - .build(); - let header = self.header().as_builder().raw(raw_header).build(); - if let Some(extension) = self.extension() { - packed::BlockV1::new_builder() - .header(header) - .uncles(self.uncles()) - .transactions(self.transactions()) - .proposals(self.proposals()) - .extension(extension) - .build() - .as_v0() - } else { - self.as_builder().header(header).build() - } - } - /// Gets the i-th extra field if it exists; i started from 0. pub fn extra_field(&self, index: usize) -> Option { let count = self.count_extra_fields(); @@ -247,27 +186,25 @@ impl packed::Block { } } -impl packed::BlockV1 { - /// Converts to a compatible [`Block`](struct.Block.html) with an extra field. - pub fn as_v0(&self) -> packed::Block { - packed::Block::new_unchecked(self.as_bytes()) +impl packed::CompactBlock { + /// Calculates the length of transactions. + pub fn txs_len(&self) -> usize { + self.prefilled_transactions().len() + self.short_ids().len() } -} -impl<'r> packed::BlockReader<'r> { /// Gets the i-th extra field if it exists; i started from 0. - pub fn extra_field(&self, index: usize) -> Option<&[u8]> { + pub fn extra_field(&self, index: usize) -> Option { let count = self.count_extra_fields(); if count > index { let slice = self.as_slice(); let i = (1 + Self::FIELD_COUNT + index) * molecule::NUMBER_SIZE; let start = molecule::unpack_number(&slice[i..]) as usize; if count == index + 1 { - Some(&self.as_slice()[start..]) + Some(self.as_bytes().slice(start..)) } else { let j = i + molecule::NUMBER_SIZE; let end = molecule::unpack_number(&slice[j..]) as usize; - Some(&self.as_slice()[start..end]) + Some(self.as_bytes().slice(start..end)) } } else { None @@ -278,127 +215,34 @@ impl<'r> packed::BlockReader<'r> { /// /// # Panics /// - /// Panics if the first extra field exists but not a valid [`BytesReader`](struct.BytesReader.html). - pub fn extension(&self) -> Option { + /// Panics if the first extra field exists but not a valid [`Bytes`](struct.Bytes.html). + pub fn extension(&self) -> Option { self.extra_field(0) - .map(|data| packed::BytesReader::from_slice(data).unwrap()) + .map(|data| packed::Bytes::from_slice(&data).unwrap()) } } -impl<'r> packed::BlockV1Reader<'r> { - /// Converts to a compatible [`BlockReader`](struct.BlockReader.html) with an extra field. - pub fn as_v0(&self) -> packed::BlockReader { - packed::BlockReader::new_unchecked(self.as_slice()) +impl packed::BlockV1 { + /// Converts to a compatible [`Block`](struct.Block.html) with an extra field. + pub fn as_v0(&self) -> packed::Block { + packed::Block::new_unchecked(self.as_bytes()) } } -impl packed::CompactBlock { - /// Builds a `CompactBlock` from block and prefilled transactions indexes. - pub fn build_from_block( - block: &core::BlockView, - prefilled_transactions_indexes: &HashSet, - ) -> Self { - // always prefill cellbase - let prefilled_transactions_len = prefilled_transactions_indexes.len() + 1; - let mut short_ids: Vec = Vec::with_capacity( - block - .data() - .transactions() - .len() - .saturating_sub(prefilled_transactions_len), - ); - let mut prefilled_transactions = Vec::with_capacity(prefilled_transactions_len); - - for (transaction_index, transaction) in block.transactions().into_iter().enumerate() { - if prefilled_transactions_indexes.contains(&transaction_index) - || transaction.is_cellbase() - { - let prefilled_tx = packed::IndexTransaction::new_builder() - .index((transaction_index as u32).pack()) - .transaction(transaction.data()) - .build(); - prefilled_transactions.push(prefilled_tx); - } else { - short_ids.push(transaction.proposal_short_id()); - } - } - - if let Some(extension) = block.data().extension() { - packed::CompactBlockV1::new_builder() - .header(block.data().header()) - .short_ids(short_ids.pack()) - .prefilled_transactions(prefilled_transactions.pack()) - .uncles(block.uncle_hashes.clone()) - .proposals(block.data().proposals()) - .extension(extension) - .build() - .as_v0() - } else { - packed::CompactBlock::new_builder() - .header(block.data().header()) - .short_ids(short_ids.pack()) - .prefilled_transactions(prefilled_transactions.pack()) - .uncles(block.uncle_hashes.clone()) - .proposals(block.data().proposals()) - .build() - } - } - - /// Takes proposal short ids for the transactions which are not prefilled. - pub fn block_short_ids(&self) -> Vec> { - let txs_len = self.txs_len(); - let mut block_short_ids: Vec> = Vec::with_capacity(txs_len); - let prefilled_indexes = self - .prefilled_transactions() - .into_iter() - .map(|tx_index| tx_index.index().unpack()) - .collect::>(); - - let mut index = 0; - for i in 0..txs_len { - if prefilled_indexes.contains(&i) { - block_short_ids.push(None); - } else { - block_short_ids.push(self.short_ids().get(index)); - index += 1; - } - } - block_short_ids - } - - /// Calculates the length of transactions. - pub fn txs_len(&self) -> usize { - self.prefilled_transactions().len() + self.short_ids().len() - } - - fn prefilled_indexes_iter(&self) -> impl Iterator { - self.prefilled_transactions() - .into_iter() - .map(|i| i.index().unpack()) - } - - /// Collects the short id indexes. - pub fn short_id_indexes(&self) -> Vec { - let prefilled_indexes: HashSet = self.prefilled_indexes_iter().collect(); - - (0..self.txs_len()) - .filter(|index| !prefilled_indexes.contains(index)) - .collect() - } - +impl<'r> packed::BlockReader<'r> { /// Gets the i-th extra field if it exists; i started from 0. - pub fn extra_field(&self, index: usize) -> Option { + pub fn extra_field(&self, index: usize) -> Option<&[u8]> { let count = self.count_extra_fields(); if count > index { let slice = self.as_slice(); let i = (1 + Self::FIELD_COUNT + index) * molecule::NUMBER_SIZE; let start = molecule::unpack_number(&slice[i..]) as usize; if count == index + 1 { - Some(self.as_bytes().slice(start..)) + Some(&self.as_slice()[start..]) } else { let j = i + molecule::NUMBER_SIZE; let end = molecule::unpack_number(&slice[j..]) as usize; - Some(self.as_bytes().slice(start..end)) + Some(&self.as_slice()[start..end]) } } else { None @@ -409,10 +253,17 @@ impl packed::CompactBlock { /// /// # Panics /// - /// Panics if the first extra field exists but not a valid [`Bytes`](struct.Bytes.html). - pub fn extension(&self) -> Option { + /// Panics if the first extra field exists but not a valid [`BytesReader`](struct.BytesReader.html). + pub fn extension(&self) -> Option { self.extra_field(0) - .map(|data| packed::Bytes::from_slice(&data).unwrap()) + .map(|data| packed::BytesReader::from_slice(data).unwrap()) + } +} + +impl<'r> packed::BlockV1Reader<'r> { + /// Converts to a compatible [`BlockReader`](struct.BlockReader.html) with an extra field. + pub fn as_v0(&self) -> packed::BlockReader { + packed::BlockReader::new_unchecked(self.as_slice()) } } @@ -436,3 +287,11 @@ impl AsRef<[u8]> for packed::TransactionKey { self.as_slice() } } + +impl packed::HeaderDigest { + /// Checks if the `HeaderDigest` is the default value. + pub fn is_default(&self) -> bool { + let default = Self::default(); + self.as_slice() == default.as_slice() + } +} diff --git a/util/types/src/extension/tests/calc_hash.rs b/util/gen-types/src/extension/tests/calc_hash.rs similarity index 95% rename from util/types/src/extension/tests/calc_hash.rs rename to util/gen-types/src/extension/tests/calc_hash.rs index d1f425261e..f80dc86d14 100644 --- a/util/types/src/extension/tests/calc_hash.rs +++ b/util/gen-types/src/extension/tests/calc_hash.rs @@ -1,4 +1,5 @@ -use crate::{h256, packed, prelude::*}; +use crate::{packed, prelude::*, vec}; +use ckb_fixed_hash::h256; use ckb_hash::blake2b_256; #[test] @@ -89,13 +90,6 @@ fn empty_uncles_hash() { assert_eq!(uncles.calc_uncles_hash(), expect.pack()); } -#[test] -fn empty_extra_hash() { - let block = packed::Block::new_builder().build(); - let expect = h256!("0x0"); - assert_eq!(block.calc_extra_hash().extra_hash(), expect.pack()); -} - #[test] fn empty_script_hash() { let script = packed::Script::new_builder().build(); diff --git a/util/types/src/extension/tests/capacity.rs b/util/gen-types/src/extension/tests/capacity.rs similarity index 91% rename from util/types/src/extension/tests/capacity.rs rename to util/gen-types/src/extension/tests/capacity.rs index 3477df839f..15da0d6c80 100644 --- a/util/types/src/extension/tests/capacity.rs +++ b/util/gen-types/src/extension/tests/capacity.rs @@ -1,8 +1,5 @@ -use crate::{ - core::{capacity_bytes, Capacity}, - packed, - prelude::*, -}; +use crate::{packed, prelude::*, vec}; +use ckb_occupied_capacity::{capacity_bytes, Capacity}; #[test] fn script_occupied_capacity() { diff --git a/util/types/src/extension/tests/check_data.rs b/util/gen-types/src/extension/tests/check_data.rs similarity index 98% rename from util/types/src/extension/tests/check_data.rs rename to util/gen-types/src/extension/tests/check_data.rs index e8f4943e42..c66c247392 100644 --- a/util/types/src/extension/tests/check_data.rs +++ b/util/gen-types/src/extension/tests/check_data.rs @@ -1,4 +1,4 @@ -use crate::{packed, prelude::*}; +use crate::{borrow::ToOwned, packed, prelude::*}; fn create_transaction( outputs: &[&packed::CellOutput], diff --git a/util/gen-types/src/extension/tests/mod.rs b/util/gen-types/src/extension/tests/mod.rs new file mode 100644 index 0000000000..e1af889604 --- /dev/null +++ b/util/gen-types/src/extension/tests/mod.rs @@ -0,0 +1,13 @@ +#[cfg(feature = "std")] +mod calc_hash; +#[cfg(feature = "std")] +mod capacity; + +#[cfg(any(feature = "check-data", feature = "std"))] +mod check_data; + +#[cfg(any(feature = "serialized-size", feature = "std"))] +mod serialized_size; + +#[cfg(feature = "std")] +mod rust_core_traits; diff --git a/util/gen-types/src/extension/tests/rust_core_traits.rs b/util/gen-types/src/extension/tests/rust_core_traits.rs new file mode 100644 index 0000000000..2d45b7d75b --- /dev/null +++ b/util/gen-types/src/extension/tests/rust_core_traits.rs @@ -0,0 +1,124 @@ +use crate::{packed::*, prelude::*}; +use ckb_fixed_hash::h256; +use numext_fixed_uint::U256; + +#[test] +fn test_uint32_cmp() { + let a: Uint32 = 10u32.pack(); + let b = 20u32.pack(); + let c = 10u32.pack(); + assert!(a < b); + assert!(a == c); +} + +#[test] +fn test_uint64_cmp() { + let a: Uint64 = 10u64.pack(); + let b = 20u64.pack(); + let c = 10u64.pack(); + assert!(a < b); + assert!(a == c); + + let a: Uint64 = 1000u64.pack(); + let b: Uint64 = 2000u64.pack(); + assert!(a < b); +} + +#[test] +fn test_uint128_cmp() { + let a: Uint128 = 10u128.pack(); + let b = 20u128.pack(); + let c = 10u128.pack(); + assert!(a < b); + assert!(a == c); +} + +#[test] +fn test_uint256_cmp() { + let a = U256::from(10u32).pack(); + let b = U256::from(20u32).pack(); + let c = U256::from(10u32).pack(); + assert!(a < b); + assert!(a == c); +} + +#[test] +fn test_byte32_cmp() { + let a = h256!("0xd1670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562").pack(); + let b = h256!("0xd2670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562").pack(); + let c = h256!("0xd1670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562").pack(); + + assert!(a < b); + assert!(a == c); +} + +#[test] +fn test_bytesopt_cmp() { + let a = Some( + h256!("0xd1670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562") + .pack() + .as_bytes(), + ) + .pack(); + let b = Some( + h256!("0xd2670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562") + .pack() + .as_bytes(), + ) + .pack(); + let c = Some( + h256!("0xd1670e45af1deb9cc00951d71c09ce80932e7ddf9fb151d744436bd04ac4a562") + .pack() + .as_bytes(), + ) + .pack(); + let d = BytesOpt::new_builder().build(); + + assert!(d.is_none()); + assert!(a < b); + assert!(a > d); + assert!(a == c); +} + +#[test] +fn test_script_cmp() { + let a = Script::new_builder().args(vec![1].pack()).build(); + let b = Script::new_builder().args(vec![2].pack()).build(); + + assert!(a < b); +} + +#[test] +fn test_celldep_cmp() { + let a = CellDep::new_builder().dep_type(1.into()).build(); + let b = CellDep::new_builder().dep_type(2.into()).build(); + assert!(a < b); +} + +#[test] +fn test_outpoint_cmp() { + let a = OutPoint::new_builder().index(1u32.pack()).build(); + let b = OutPoint::new_builder().index(2u32.pack()).build(); + assert!(a < b); +} + +#[test] +fn test_cellinput_cmp() { + let a = CellInput::new_builder().since(1000u64.pack()).build(); + let b = CellInput::new_builder().since(2000u64.pack()).build(); + assert!(a > b); +} + +#[test] +fn test_celloutput_cmp() { + let script_lock = Script::new_builder().hash_type(1.into()).build(); + let script_type = Script::new_builder().hash_type(2.into()).build(); + let script_type_opt = ScriptOpt::new_builder().set(Some(script_type)).build(); + let output_a = CellOutput::new_builder().lock(script_lock.clone()).build(); + let output_b = CellOutput::new_builder() + .lock(script_lock) + .type_(script_type_opt) + .build(); + + assert!(output_a < output_b); +} diff --git a/util/types/src/extension/tests/serialized_size.rs b/util/gen-types/src/extension/tests/serialized_size.rs similarity index 98% rename from util/types/src/extension/tests/serialized_size.rs rename to util/gen-types/src/extension/tests/serialized_size.rs index 250c9cac5f..c770568b6f 100644 --- a/util/types/src/extension/tests/serialized_size.rs +++ b/util/gen-types/src/extension/tests/serialized_size.rs @@ -1,4 +1,4 @@ -use crate::{packed, prelude::*}; +use crate::{packed, prelude::*, vec, vec::Vec}; #[test] fn block_size_should_not_include_uncles_proposals() { diff --git a/util/types/src/generated/blockchain.rs b/util/gen-types/src/generated/blockchain.rs similarity index 100% rename from util/types/src/generated/blockchain.rs rename to util/gen-types/src/generated/blockchain.rs diff --git a/util/types/src/generated/extensions.rs b/util/gen-types/src/generated/extensions.rs similarity index 100% rename from util/types/src/generated/extensions.rs rename to util/gen-types/src/generated/extensions.rs diff --git a/util/types/src/generated/mod.rs b/util/gen-types/src/generated/mod.rs similarity index 100% rename from util/types/src/generated/mod.rs rename to util/gen-types/src/generated/mod.rs diff --git a/util/types/src/generated/protocols.rs b/util/gen-types/src/generated/protocols.rs similarity index 100% rename from util/types/src/generated/protocols.rs rename to util/gen-types/src/generated/protocols.rs diff --git a/util/gen-types/src/lib.rs b/util/gen-types/src/lib.rs new file mode 100644 index 0000000000..c2aa5c5183 --- /dev/null +++ b/util/gen-types/src/lib.rs @@ -0,0 +1,26 @@ +//! # The Generated Types Library +//! +//! This Library provides the generated types for CKB. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +mod conversion; +pub mod core; +mod extension; +mod generated; +pub mod prelude; +pub use generated::packed; + +//re-exports +pub use molecule::bytes; + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + use std::{vec, borrow}; + } else { + use alloc::{vec, borrow}; + } +} diff --git a/util/gen-types/src/prelude.rs b/util/gen-types/src/prelude.rs new file mode 100644 index 0000000000..8f5d3e7457 --- /dev/null +++ b/util/gen-types/src/prelude.rs @@ -0,0 +1,84 @@ +//! This module includes several traits. +//! +//! Few traits are re-exported from other crates, few are used as aliases and others are syntactic sugar. + +pub use molecule::{ + hex_string, + prelude::{Builder, Entity, Reader}, +}; + +/// An alias of `unwrap()` to mark where we are really have confidence to do unwrap. +/// +/// We can also customize the panic message or do something else in this alias. +pub trait ShouldBeOk { + /// Unwraps an `Option` or a `Result` with confidence and we assume that it's impossible to fail. + fn should_be_ok(self) -> T; +} + +// Use for Option +impl ShouldBeOk for Option { + fn should_be_ok(self) -> T { + self.unwrap_or_else(|| panic!("should not be None")) + } +} + +// Use for verify +impl ShouldBeOk for molecule::error::VerificationResult { + fn should_be_ok(self) -> T { + self.unwrap_or_else(|err| panic!("verify slice should be ok, but {err}")) + } +} + +/// An alias of `from_slice(..)` to mark where we are really have confidence to do unwrap on the result of `from_slice(..)`. +pub trait FromSliceShouldBeOk<'r>: Reader<'r> { + /// Unwraps the result of `from_slice(..)` with confidence and we assume that it's impossible to fail. + fn from_slice_should_be_ok(slice: &'r [u8]) -> Self; + + /// Unwraps the result of `from_compatible_slice(..)` with confidence and we assume that it's impossible to fail. + fn from_compatible_slice_should_be_ok(slice: &'r [u8]) -> Self; +} + +impl<'r, R> FromSliceShouldBeOk<'r> for R +where + R: Reader<'r>, +{ + fn from_slice_should_be_ok(slice: &'r [u8]) -> Self { + match Self::from_slice(slice) { + Ok(ret) => ret, + Err(err) => panic!( + "failed to convert from slice: reason: {}; data: 0x{}.", + err, + hex_string(slice) + ), + } + } + + fn from_compatible_slice_should_be_ok(slice: &'r [u8]) -> Self { + match Self::from_compatible_slice(slice) { + Ok(ret) => ret, + Err(err) => panic!( + "failed to convert from slice: reason: {}; data: 0x{}.", + err, + hex_string(slice) + ), + } + } +} + +/// A syntactic sugar to convert binary data into rust types. +pub trait Unpack { + /// Unpack binary data into rust types. + fn unpack(&self) -> T; +} + +/// A syntactic sugar to convert a rust type into binary data. +pub trait Pack { + /// Packs a rust type into binary data. + fn pack(&self) -> T; +} + +/// A syntactic sugar to convert a vector of binary data into one binary data. +pub trait PackVec: IntoIterator { + /// Packs a vector of binary data into one binary data. + fn pack(self) -> T; +} diff --git a/util/hash/Cargo.toml b/util/hash/Cargo.toml index d1af80c423..53d79c81d4 100644 --- a/util/hash/Cargo.toml +++ b/util/hash/Cargo.toml @@ -8,8 +8,12 @@ description = "CKB default hash function." homepage = "https://github.com/nervosnetwork/ckb" repository = "https://github.com/nervosnetwork/ckb" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -blake2b-rs = "0.2" +[features] +default = [] +ckb-contract = [] # This feature is used for CKB contract development -[target.'cfg(target_arch = "wasm32")'.dependencies] -blake2b-ref = "0.2.0" +[target.'cfg(not(any(target_arch = "wasm32", features = "ckb-contract")))'.dependencies] +blake2b = { package = "blake2b-rs", version = "0.2" } + +[target.'cfg(any(target_arch = "wasm32", features = "ckb-contract"))'.dependencies] +blake2b = { package = "blake2b-ref", version = "0.2.0" } diff --git a/util/hash/src/lib.rs b/util/hash/src/lib.rs index fc6275c78a..5debb844e1 100644 --- a/util/hash/src/lib.rs +++ b/util/hash/src/lib.rs @@ -9,10 +9,7 @@ #![no_std] -#[cfg(target_arch = "wasm32")] -pub use blake2b_ref::{Blake2b, Blake2bBuilder}; -#[cfg(not(target_arch = "wasm32"))] -pub use blake2b_rs::{Blake2b, Blake2bBuilder}; +pub use blake2b::{Blake2b, Blake2bBuilder}; #[doc(hidden)] pub const BLAKE2B_KEY: &[u8] = &[]; diff --git a/util/light-client-protocol-server/src/tests/components/get_last_state_proof.rs b/util/light-client-protocol-server/src/tests/components/get_last_state_proof.rs index 11e2c4af48..6507668741 100644 --- a/util/light-client-protocol-server/src/tests/components/get_last_state_proof.rs +++ b/util/light-client-protocol-server/src/tests/components/get_last_state_proof.rs @@ -3,7 +3,7 @@ use ckb_network::{CKBProtocolHandler, PeerIndex, SupportProtocols}; use ckb_types::{ packed, prelude::*, - utilities::merkle_mountain_range::{MMRProof, VerifiableHeader}, + utilities::merkle_mountain_range::{HeaderDigest, MMRProof, VerifiableHeader}, }; use crate::tests::{ diff --git a/util/types/Cargo.toml b/util/types/Cargo.toml index 2b974f1e83..bc8ddb1dd5 100644 --- a/util/types/Cargo.toml +++ b/util/types/Cargo.toml @@ -18,6 +18,7 @@ ckb-occupied-capacity = { path = "../occupied-capacity", version = "= 0.112.0-pr ckb-hash = { path = "../hash", version = "= 0.112.0-pre" } ckb-channel = { path = "../channel", version = "= 0.112.0-pre" } ckb-constant = { path = "../constant", version = "= 0.112.0-pre" } +ckb-gen-types = { path = "../gen-types", version = "= 0.112.0-pre" } bit-vec = "0.6.3" ckb-error = { path = "../../error", version = "= 0.112.0-pre" } ckb-rational = { path = "../rational", version = "= 0.112.0-pre" } diff --git a/util/types/src/conversion/blockchain.rs b/util/types/src/conversion/blockchain.rs index b9a0a4fe5e..65b1e0cc72 100644 --- a/util/types/src/conversion/blockchain.rs +++ b/util/types/src/conversion/blockchain.rs @@ -1,84 +1,4 @@ -use crate::{ - bytes::Bytes, - core::{self, Capacity}, - packed, - prelude::*, - H256, U256, -}; - -impl Pack for Capacity { - fn pack(&self) -> packed::Uint64 { - self.as_u64().pack() - } -} - -impl<'r> Unpack for packed::Uint64Reader<'r> { - fn unpack(&self) -> core::Capacity { - Capacity::shannons(self.unpack()) - } -} -impl_conversion_for_entity_unpack!(Capacity, Uint64); - -impl Pack for U256 { - fn pack(&self) -> packed::Uint256 { - packed::Uint256::from_slice(&self.to_le_bytes()[..]).expect("impossible: fail to pack U256") - } -} - -impl<'r> Unpack for packed::Uint256Reader<'r> { - fn unpack(&self) -> U256 { - U256::from_little_endian(self.as_slice()).expect("internal error: fail to unpack U256") - } -} -impl_conversion_for_entity_unpack!(U256, Uint256); - -impl Pack for H256 { - fn pack(&self) -> packed::Byte32 { - packed::Byte32::from_slice(self.as_bytes()).expect("impossible: fail to pack H256") - } -} - -impl<'r> Unpack for packed::Byte32Reader<'r> { - fn unpack(&self) -> H256 { - H256::from_slice(self.as_slice()).expect("internal error: fail to unpack H256") - } -} -impl_conversion_for_entity_unpack!(H256, Byte32); - -impl Pack for [u8; 32] { - fn pack(&self) -> packed::Byte32 { - packed::Byte32::from_slice(&self[..]).expect("impossible: fail to pack [u8; 32]") - } -} - -impl Pack for [u8; 10] { - fn pack(&self) -> packed::ProposalShortId { - packed::ProposalShortId::from_slice(&self[..]) - .expect("impossible: fail to pack to ProposalShortId") - } -} - -impl Pack for Bytes { - fn pack(&self) -> packed::Bytes { - let len = (self.len() as u32).to_le_bytes(); - let mut v = Vec::with_capacity(4 + self.len()); - v.extend_from_slice(&len[..]); - v.extend_from_slice(&self[..]); - packed::Bytes::new_unchecked(v.into()) - } -} - -impl<'r> Unpack for packed::BytesReader<'r> { - fn unpack(&self) -> Bytes { - Bytes::from(self.raw_data().to_owned()) - } -} - -impl Unpack for packed::Bytes { - fn unpack(&self) -> Bytes { - self.raw_data() - } -} +use crate::{core, packed, prelude::*}; impl Pack for core::EpochNumberWithFraction { fn pack(&self) -> packed::Uint64 { @@ -92,20 +12,3 @@ impl<'r> Unpack for packed::Uint64Reader<'r> { } } impl_conversion_for_entity_unpack!(core::EpochNumberWithFraction, Uint64); - -impl_conversion_for_option!(H256, Byte32Opt, Byte32OptReader); -impl_conversion_for_vector!(Capacity, Uint64Vec, Uint64VecReader); -impl_conversion_for_vector!(Bytes, BytesVec, BytesVecReader); -impl_conversion_for_packed_optional_pack!(Byte32, Byte32Opt); -impl_conversion_for_packed_optional_pack!(CellOutput, CellOutputOpt); -impl_conversion_for_packed_optional_pack!(Script, ScriptOpt); -impl_conversion_for_packed_iterator_pack!(ProposalShortId, ProposalShortIdVec); -impl_conversion_for_packed_iterator_pack!(Bytes, BytesVec); -impl_conversion_for_packed_iterator_pack!(Transaction, TransactionVec); -impl_conversion_for_packed_iterator_pack!(OutPoint, OutPointVec); -impl_conversion_for_packed_iterator_pack!(CellDep, CellDepVec); -impl_conversion_for_packed_iterator_pack!(CellOutput, CellOutputVec); -impl_conversion_for_packed_iterator_pack!(CellInput, CellInputVec); -impl_conversion_for_packed_iterator_pack!(UncleBlock, UncleBlockVec); -impl_conversion_for_packed_iterator_pack!(Header, HeaderVec); -impl_conversion_for_packed_iterator_pack!(Byte32, Byte32Vec); diff --git a/util/types/src/conversion/mod.rs b/util/types/src/conversion/mod.rs index 10b189707e..d6fc636dd8 100644 --- a/util/types/src/conversion/mod.rs +++ b/util/types/src/conversion/mod.rs @@ -8,6 +8,4 @@ mod utilities; mod blockchain; -mod network; -mod primitive; mod storage; diff --git a/util/types/src/conversion/utilities.rs b/util/types/src/conversion/utilities.rs index cd5faa9a78..0fbdc5bd98 100644 --- a/util/types/src/conversion/utilities.rs +++ b/util/types/src/conversion/utilities.rs @@ -7,92 +7,3 @@ macro_rules! impl_conversion_for_entity_unpack { } }; } - -macro_rules! impl_conversion_for_option_pack { - ($original:ty, $entity:ident) => { - impl Pack for Option<$original> { - fn pack(&self) -> packed::$entity { - if let Some(ref inner) = self { - packed::$entity::new_unchecked(inner.pack().as_bytes()) - } else { - packed::$entity::default() - } - } - } - }; -} - -macro_rules! impl_conversion_for_option_unpack { - ($original:ty, $entity:ident, $reader:ident) => { - impl<'r> Unpack> for packed::$reader<'r> { - fn unpack(&self) -> Option<$original> { - self.to_opt().map(|x| x.unpack()) - } - } - impl_conversion_for_entity_unpack!(Option<$original>, $entity); - }; -} - -macro_rules! impl_conversion_for_option { - ($original:ty, $entity:ident, $reader:ident) => { - impl_conversion_for_option_pack!($original, $entity); - impl_conversion_for_option_unpack!($original, $entity, $reader); - }; -} - -macro_rules! impl_conversion_for_vector_pack { - ($original:ty, $entity:ident) => { - impl Pack for [$original] { - fn pack(&self) -> packed::$entity { - packed::$entity::new_builder() - .set(self.iter().map(|v| v.pack()).collect()) - .build() - } - } - }; -} - -macro_rules! impl_conversion_for_vector_unpack { - ($original:ty, $entity:ident, $reader:ident) => { - impl<'r> Unpack> for packed::$reader<'r> { - fn unpack(&self) -> Vec<$original> { - self.iter().map(|x| x.unpack()).collect() - } - } - impl_conversion_for_entity_unpack!(Vec<$original>, $entity); - }; -} - -macro_rules! impl_conversion_for_vector { - ($original:ty, $entity:ident, $reader:ident) => { - impl_conversion_for_vector_pack!($original, $entity); - impl_conversion_for_vector_unpack!($original, $entity, $reader); - }; -} - -macro_rules! impl_conversion_for_packed_optional_pack { - ($original:ident, $entity:ident) => { - impl Pack for Option { - fn pack(&self) -> packed::$entity { - if let Some(ref inner) = self { - packed::$entity::new_unchecked(inner.as_bytes()) - } else { - packed::$entity::default() - } - } - } - }; -} - -macro_rules! impl_conversion_for_packed_iterator_pack { - ($item:ident, $vec:ident) => { - impl PackVec for T - where - T: IntoIterator, - { - fn pack(self) -> packed::$vec { - packed::$vec::new_builder().extend(self).build() - } - } - }; -} diff --git a/util/types/src/core/advanced_builders.rs b/util/types/src/core/advanced_builders.rs index 15671857f3..9e2a8e2343 100644 --- a/util/types/src/core/advanced_builders.rs +++ b/util/types/src/core/advanced_builders.rs @@ -467,9 +467,9 @@ impl BlockBuilder { * Convert a struct to an advanced builder */ -impl packed::Transaction { +impl AsTransactionBuilder for packed::Transaction { /// Creates an advanced builder base on current data. - pub fn as_advanced_builder(&self) -> TransactionBuilder { + fn as_advanced_builder(&self) -> TransactionBuilder { TransactionBuilder::default() .version(self.raw().version()) .cell_deps(self.raw().cell_deps()) @@ -481,9 +481,9 @@ impl packed::Transaction { } } -impl packed::Header { +impl AsHeaderBuilder for packed::Header { /// Creates an advanced builder base on current data. - pub fn as_advanced_builder(&self) -> HeaderBuilder { + fn as_advanced_builder(&self) -> HeaderBuilder { HeaderBuilder::default() .version(self.raw().version()) .parent_hash(self.raw().parent_hash()) @@ -499,14 +499,14 @@ impl packed::Header { } } -impl packed::Block { +impl AsBlockBuilder for packed::Block { /// Creates an empty advanced builder. - pub fn new_advanced_builder() -> BlockBuilder { + fn new_advanced_builder() -> BlockBuilder { Default::default() } /// Creates an advanced builder base on current data. - pub fn as_advanced_builder(&self) -> BlockBuilder { + fn as_advanced_builder(&self) -> BlockBuilder { BlockBuilder::default() .header(self.header().into_view()) .uncles( diff --git a/util/types/src/core/blockchain.rs b/util/types/src/core/blockchain.rs index 9dc2e7cb73..670fd2d90e 100644 --- a/util/types/src/core/blockchain.rs +++ b/util/types/src/core/blockchain.rs @@ -2,87 +2,15 @@ use ckb_error::OtherError; use crate::packed; -/// Specifies how the script `code_hash` is used to match the script code and how to run the code. -/// The hash type is split into the high 7 bits and the low 1 bit, -/// when the low 1 bit is 1, it indicates the type, -/// when the low 1 bit is 0, it indicates the data, -/// and then it relies on the high 7 bits to indicate -/// that the data actually corresponds to the version. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub enum ScriptHashType { - /// Type "data" matches script code via cell data hash, and run the script code in v0 CKB VM. - Data = 0, - /// Type "type" matches script code via cell type script hash. - Type = 1, - /// Type "data1" matches script code via cell data hash, and run the script code in v1 CKB VM. - Data1 = 2, - /// Type "data2" matches script code via cell data hash, and run the script code in v2 CKB VM. - Data2 = 4, -} - -impl Default for ScriptHashType { - fn default() -> Self { - ScriptHashType::Data - } -} - -impl TryFrom for ScriptHashType { - type Error = OtherError; - - fn try_from(v: u8) -> Result { - match v { - 0 => Ok(ScriptHashType::Data), - 1 => Ok(ScriptHashType::Type), - 2 => Ok(ScriptHashType::Data1), - 4 => Ok(ScriptHashType::Data2), - _ => Err(OtherError::new(format!("Invalid script hash type {v}"))), - } - } -} - -impl TryFrom for ScriptHashType { - type Error = OtherError; - - fn try_from(v: packed::Byte) -> Result { - Into::::into(v).try_into() - } -} - -impl ScriptHashType { - #[inline] - pub(crate) fn verify_value(v: u8) -> bool { - v <= 4 && v != 3 - } -} - -impl Into for ScriptHashType { - #[inline] - fn into(self) -> u8 { - match self { - Self::Data => 0, - Self::Type => 1, - Self::Data1 => 2, - Self::Data2 => 4, - } - } -} - -impl Into for ScriptHashType { - #[inline] - fn into(self) -> packed::Byte { - Into::::into(self).into() - } -} - -/// TODO(doc): @quake +/// The DepType enum represents different types of dependencies for `cell_deps`. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum DepType { - /// TODO(doc): @quake + /// Code dependency: The cell provides code directly Code = 0, - /// TODO(doc): @quake + /// Dependency group: The cell bundles several cells as its members + /// which will be expanded inside `cell_deps`. DepGroup = 1, } - impl Default for DepType { fn default() -> Self { DepType::Code @@ -107,17 +35,9 @@ impl Into for DepType { self as u8 } } - impl Into for DepType { #[inline] fn into(self) -> packed::Byte { (self as u8).into() } } - -impl DepType { - #[inline] - pub(crate) fn verify_value(v: u8) -> bool { - v <= 1 - } -} diff --git a/util/types/src/core/error.rs b/util/types/src/core/error.rs index 1bd60e0d6d..16cfbccd8d 100644 --- a/util/types/src/core/error.rs +++ b/util/types/src/core/error.rs @@ -1,7 +1,9 @@ //! The error types to unexpected out-points. -use crate::core::{Capacity, Version}; -use crate::generated::packed::{Byte32, OutPoint}; +use crate::{ + core::{Capacity, Version}, + packed::{Byte32, OutPoint}, +}; use ckb_error::{impl_error_conversion_with_kind, prelude::*, Error, ErrorKind}; use derive_more::Display; diff --git a/util/types/src/core/mod.rs b/util/types/src/core/mod.rs index c3fe1e9425..1e7342d57c 100644 --- a/util/types/src/core/mod.rs +++ b/util/types/src/core/mod.rs @@ -28,7 +28,7 @@ mod reward; mod transaction_meta; mod views; pub use advanced_builders::{BlockBuilder, HeaderBuilder, TransactionBuilder}; -pub use blockchain::{DepType, ScriptHashType}; +pub use blockchain::DepType; pub use extras::{BlockExt, EpochExt, EpochNumberWithFraction, TransactionInfo}; pub use fee_rate::FeeRate; pub use reward::{BlockEconomicState, BlockIssuance, BlockReward, MinerReward}; @@ -38,6 +38,7 @@ pub use views::{ BlockView, ExtraHashView, HeaderView, TransactionView, UncleBlockVecView, UncleBlockView, }; +pub use ckb_gen_types::core::*; pub use ckb_occupied_capacity::{ capacity_bytes, Capacity, Error as CapacityError, Ratio, Result as CapacityResult, }; diff --git a/util/types/src/core/transaction_meta.rs b/util/types/src/core/transaction_meta.rs index 749ab17ec8..b2e0acd8f4 100644 --- a/util/types/src/core/transaction_meta.rs +++ b/util/types/src/core/transaction_meta.rs @@ -1,6 +1,5 @@ -use bit_vec::BitVec; - use crate::packed::Byte32; +use bit_vec::BitVec; /// TODO(doc): @quake #[derive(Default, Debug, PartialEq, Eq, Clone)] diff --git a/util/types/src/core/tx_pool.rs b/util/types/src/core/tx_pool.rs index a1f182905d..30e6fa197e 100644 --- a/util/types/src/core/tx_pool.rs +++ b/util/types/src/core/tx_pool.rs @@ -1,10 +1,13 @@ //! Tx-pool shared type define. -use crate::core::{ - error::{OutPointError, TransactionError}, - BlockNumber, Capacity, Cycle, FeeRate, +use crate::{ + core::{ + self, + error::{OutPointError, TransactionError}, + BlockNumber, Capacity, Cycle, FeeRate, + }, + packed::Byte32, + H256, }; -use crate::packed::Byte32; -use crate::{core, H256}; use ckb_error::{ impl_error_conversion_with_kind, prelude::*, Error, ErrorKind, InternalError, InternalErrorKind, }; diff --git a/util/types/src/core/views.rs b/util/types/src/core/views.rs index c53fe93f32..6e7a473087 100644 --- a/util/types/src/core/views.rs +++ b/util/types/src/core/views.rs @@ -869,11 +869,8 @@ impl BlockView { * Convert packed bytes wrappers to views. */ -impl packed::Transaction { - /// Calculates the associated hashes and converts into [`TransactionView`] with those hashes. - /// - /// [`TransactionView`]: ../core/struct.TransactionView.html - pub fn into_view(self) -> TransactionView { +impl IntoTransactionView for packed::Transaction { + fn into_view(self) -> TransactionView { let hash = self.calc_tx_hash(); let witness_hash = self.calc_witness_hash(); TransactionView { @@ -884,27 +881,27 @@ impl packed::Transaction { } } -impl packed::Header { +impl IntoHeaderView for packed::Header { /// Calculates the header hash and converts into [`HeaderView`] with the hash. /// /// [`HeaderView`]: ../core/struct.HeaderView.html - pub fn into_view(self) -> HeaderView { + fn into_view(self) -> HeaderView { let hash = self.calc_header_hash(); HeaderView { data: self, hash } } } -impl packed::UncleBlock { +impl IntoUncleBlockView for packed::UncleBlock { /// Calculates the header hash and converts into [`UncleBlockView`] with the hash. /// /// [`UncleBlockView`]: ../core/struct.UncleBlockView.html - pub fn into_view(self) -> UncleBlockView { + fn into_view(self) -> UncleBlockView { let hash = self.calc_header_hash(); UncleBlockView { data: self, hash } } } -impl packed::Block { +impl IntoBlockView for packed::Block { /// Calculates transaction associated hashes and converts them into [`BlockView`]. /// /// # Notice @@ -913,7 +910,7 @@ impl packed::Block { /// invalid merkle roots in the header. /// /// [`BlockView`]: ../core/struct.BlockView.html - pub fn into_view_without_reset_header(self) -> BlockView { + fn into_view_without_reset_header(self) -> BlockView { let tx_hashes = self.calc_tx_hashes(); let tx_witness_hashes = self.calc_tx_witness_hashes(); Self::block_into_view_internal(self, tx_hashes, tx_witness_hashes) @@ -922,7 +919,7 @@ impl packed::Block { /// Calculates transaction associated hashes, resets all hashes and merkle roots in the header, then converts them into [`BlockView`]. /// /// [`BlockView`]: ../core/struct.BlockView.html - pub fn into_view(self) -> BlockView { + fn into_view(self) -> BlockView { let tx_hashes = self.calc_tx_hashes(); let tx_witness_hashes = self.calc_tx_witness_hashes(); let block = self.reset_header_with_hashes(&tx_hashes[..], &tx_witness_hashes[..]); diff --git a/util/types/src/extension.rs b/util/types/src/extension.rs new file mode 100644 index 0000000000..2711c8f90d --- /dev/null +++ b/util/types/src/extension.rs @@ -0,0 +1,185 @@ +use std::collections::HashSet; + +use crate::{ + core::{self}, + packed, + prelude::*, + utilities::{compact_to_difficulty, merkle_root}, + U256, +}; + +impl Difficulty for packed::RawHeader { + /// Calculates the difficulty from compact target. + fn difficulty(&self) -> U256 { + compact_to_difficulty(self.compact_target().unpack()) + } +} + +impl Difficulty for packed::Header { + /// Calculates the difficulty from compact target. + fn difficulty(&self) -> U256 { + self.raw().difficulty() + } +} + +impl ResetBlock for packed::Block { + /// Recalculates all hashes and merkle roots in the header. + fn reset_header(self) -> packed::Block { + let tx_hashes = self.as_reader().calc_tx_hashes(); + let tx_witness_hashes = self.as_reader().calc_tx_witness_hashes(); + self.reset_header_with_hashes(&tx_hashes[..], &tx_witness_hashes[..]) + } + + fn reset_header_with_hashes( + self, + tx_hashes: &[packed::Byte32], + tx_witness_hashes: &[packed::Byte32], + ) -> packed::Block { + let raw_transactions_root = merkle_root(tx_hashes); + let witnesses_root = merkle_root(tx_witness_hashes); + let transactions_root = merkle_root(&[raw_transactions_root, witnesses_root]); + let proposals_hash = self.as_reader().calc_proposals_hash(); + let extra_hash = self.as_reader().calc_extra_hash().extra_hash(); + let raw_header = self + .header() + .raw() + .as_builder() + .transactions_root(transactions_root) + .proposals_hash(proposals_hash) + .extra_hash(extra_hash) + .build(); + let header = self.header().as_builder().raw(raw_header).build(); + if let Some(extension) = self.extension() { + packed::BlockV1::new_builder() + .header(header) + .uncles(self.uncles()) + .transactions(self.transactions()) + .proposals(self.proposals()) + .extension(extension) + .build() + .as_v0() + } else { + self.as_builder().header(header).build() + } + } +} + +impl BuildCompactBlock for packed::CompactBlock { + /// Builds a `CompactBlock` from block and prefilled transactions indexes. + fn build_from_block( + block: &core::BlockView, + prefilled_transactions_indexes: &HashSet, + ) -> packed::CompactBlock { + // always prefill cellbase + let prefilled_transactions_len = prefilled_transactions_indexes.len() + 1; + let mut short_ids: Vec = Vec::with_capacity( + block + .data() + .transactions() + .len() + .saturating_sub(prefilled_transactions_len), + ); + let mut prefilled_transactions = Vec::with_capacity(prefilled_transactions_len); + + for (transaction_index, transaction) in block.transactions().into_iter().enumerate() { + if prefilled_transactions_indexes.contains(&transaction_index) + || transaction.is_cellbase() + { + let prefilled_tx = packed::IndexTransaction::new_builder() + .index((transaction_index as u32).pack()) + .transaction(transaction.data()) + .build(); + prefilled_transactions.push(prefilled_tx); + } else { + short_ids.push(transaction.proposal_short_id()); + } + } + + if let Some(extension) = block.data().extension() { + packed::CompactBlockV1::new_builder() + .header(block.data().header()) + .short_ids(short_ids.pack()) + .prefilled_transactions(prefilled_transactions.pack()) + .uncles(block.uncle_hashes.clone()) + .proposals(block.data().proposals()) + .extension(extension) + .build() + .as_v0() + } else { + packed::CompactBlock::new_builder() + .header(block.data().header()) + .short_ids(short_ids.pack()) + .prefilled_transactions(prefilled_transactions.pack()) + .uncles(block.uncle_hashes.clone()) + .proposals(block.data().proposals()) + .build() + } + } + + /// Takes proposal short ids for the transactions which are not prefilled. + fn block_short_ids(&self) -> Vec> { + let txs_len = self.txs_len(); + let mut block_short_ids: Vec> = Vec::with_capacity(txs_len); + let prefilled_indexes = self + .prefilled_transactions() + .into_iter() + .map(|tx_index| tx_index.index().unpack()) + .collect::>(); + + let mut index = 0; + for i in 0..txs_len { + if prefilled_indexes.contains(&i) { + block_short_ids.push(None); + } else { + block_short_ids.push(self.short_ids().get(index)); + index += 1; + } + } + block_short_ids + } + + /// Collects the short id indexes. + fn short_id_indexes(&self) -> Vec { + let prefilled_indexes_iter = self + .prefilled_transactions() + .into_iter() + .map(|i| i.index().unpack()); + + let prefilled_indexes: HashSet = prefilled_indexes_iter.collect(); + + (0..self.txs_len()) + .filter(|index| !prefilled_indexes.contains(index)) + .collect() + } +} + +impl<'r> CalcExtraHash for packed::BlockReader<'r> { + /// Calculates the extra hash, which is a combination of the uncles hash and + /// the extension hash. + /// + /// - If there is no extension, extra hash is the same as the uncles hash. + /// - If there is a extension, then extra hash it the hash of the combination + /// of uncles hash and the extension hash. + fn calc_extra_hash(&self) -> core::ExtraHashView { + crate::core::ExtraHashView::new(self.calc_uncles_hash(), self.calc_extension_hash()) + } +} + +impl CalcExtraHash for packed::Block { + /// Calls [`BlockReader.calc_extra_hash()`](struct.BlockReader.html#method.calc_extra_hash) + /// for [`self.as_reader()`](struct.Block.html#method.as_reader). + fn calc_extra_hash(&self) -> core::ExtraHashView { + self.as_reader().calc_extra_hash() + } +} + +#[cfg(test)] +mod test { + use crate::{h256, packed, prelude::*}; + #[test] + fn empty_extra_hash() { + let block = packed::Block::new_builder().build(); + let expect = h256!("0x0"); + assert_eq!(block.calc_extra_hash().extra_hash(), expect.pack()); + } +} diff --git a/util/types/src/extension/mod.rs b/util/types/src/extension/mod.rs deleted file mode 100644 index 7068d14982..0000000000 --- a/util/types/src/extension/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Extensions for packed bytes wrappers. -//! -//! Add methods for packed bytes wrappers. -//! -//! ### Warning -//! -//! No definitions for structs, enums is allowed. - -mod calc_hash; -mod capacity; -mod check_data; -mod serialized_size; -mod shortcuts; -mod std_traits; - -#[cfg(test)] -mod tests; diff --git a/util/types/src/extension/std_traits.rs b/util/types/src/extension/std_traits.rs deleted file mode 100644 index 38b7c8c315..0000000000 --- a/util/types/src/extension/std_traits.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::{packed, prelude::*}; - -macro_rules! impl_std_cmp_eq_and_hash { - ($struct:ident) => { - impl PartialEq for packed::$struct { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.as_slice() == other.as_slice() - } - } - impl Eq for packed::$struct {} - - impl ::std::hash::Hash for packed::$struct { - #[inline] - fn hash(&self, state: &mut H) { - state.write(self.as_slice()) - } - } - }; -} - -impl_std_cmp_eq_and_hash!(Byte32); -impl_std_cmp_eq_and_hash!(Uint256); -impl_std_cmp_eq_and_hash!(ProposalShortId); -impl_std_cmp_eq_and_hash!(Script); -impl_std_cmp_eq_and_hash!(CellDep); -impl_std_cmp_eq_and_hash!(OutPoint); -impl_std_cmp_eq_and_hash!(CellInput); -impl_std_cmp_eq_and_hash!(CellOutput); -impl_std_cmp_eq_and_hash!(Alert); -impl_std_cmp_eq_and_hash!(UncleBlock); -impl_std_cmp_eq_and_hash!(Block); -impl_std_cmp_eq_and_hash!(HeaderDigest); - -impl ::std::cmp::Ord for packed::Byte32 { - #[inline] - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - self.as_slice().cmp(other.as_slice()) - } -} - -impl ::std::cmp::PartialOrd for packed::Byte32 { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} diff --git a/util/types/src/extension/tests/mod.rs b/util/types/src/extension/tests/mod.rs deleted file mode 100644 index 77cf886ce8..0000000000 --- a/util/types/src/extension/tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod calc_hash; -mod capacity; -mod check_data; -mod serialized_size; diff --git a/util/types/src/lib.rs b/util/types/src/lib.rs index eecd41bddf..3e823f91e3 100644 --- a/util/types/src/lib.rs +++ b/util/types/src/lib.rs @@ -6,12 +6,10 @@ pub mod prelude; pub use bytes; pub use ckb_fixed_hash::{h160, h256, H160, H256}; +pub use ckb_gen_types::packed; pub use molecule::{self, error}; pub use numext_fixed_uint::{u256, U128, U256}; -mod generated; - -pub use generated::packed; pub mod core; pub mod constants; diff --git a/util/types/src/prelude.rs b/util/types/src/prelude.rs index 954e2c3da0..f9c2a228b6 100644 --- a/util/types/src/prelude.rs +++ b/util/types/src/prelude.rs @@ -1,86 +1,112 @@ //! This module includes several traits. //! //! Few traits are re-exported from other crates, few are used as aliases and others are syntactic sugar. +//! -pub use molecule::{ - hex_string, - prelude::{Builder, Entity, Reader}, +pub use crate::utilities::merkle_mountain_range::ProverMessageBuilder; +use crate::{ + core::{ + BlockBuilder, BlockView, ExtraHashView, HeaderBuilder, HeaderView, TransactionBuilder, + TransactionView, UncleBlockView, + }, + packed, U256, }; -pub use crate::utilities::merkle_mountain_range::ProverMessageBuilder; +pub use ckb_gen_types::prelude::*; + +use std::collections::HashSet; -/// An alias of `unwrap()` to mark where we are really have confidence to do unwrap. -/// -/// We can also customize the panic message or do something else in this alias. -pub trait ShouldBeOk { - /// Unwraps an `Option` or a `Result` with confidence and we assume that it's impossible to fail. - fn should_be_ok(self) -> T; +/// Trait for converting types into `TransactionView`. +pub trait IntoTransactionView { + /// Converts the implementing type into a `TransactionView`. + fn into_view(self) -> TransactionView; } -// Use for Option -impl ShouldBeOk for Option { - fn should_be_ok(self) -> T { - self.unwrap_or_else(|| panic!("should not be None")) - } +/// Trait for converting types into `HeaderView`. +pub trait IntoHeaderView { + /// Converts the implementing type into a `HeaderView`. + fn into_view(self) -> HeaderView; } -// Use for verify -impl ShouldBeOk for molecule::error::VerificationResult { - fn should_be_ok(self) -> T { - self.unwrap_or_else(|err| panic!("verify slice should be ok, but {err}")) - } +/// Trait for converting types into `UncleBlockView`. +pub trait IntoUncleBlockView { + /// Converts the implementing type into an `UncleBlockView`. + fn into_view(self) -> UncleBlockView; +} + +/// Trait for converting types into `BlockView`. +pub trait IntoBlockView { + /// Converts the implementing type into a `BlockView` without resetting the header. + fn into_view_without_reset_header(self) -> BlockView; + + /// Converts the implementing type into a `BlockView`. + fn into_view(self) -> BlockView; + + /// Converts a packed block and associated data into a `BlockView`. + fn block_into_view_internal( + block: packed::Block, + tx_hashes: Vec, + tx_witness_hashes: Vec, + ) -> BlockView; } -/// An alias of `from_slice(..)` to mark where we are really have confidence to do unwrap on the result of `from_slice(..)`. -pub trait FromSliceShouldBeOk<'r>: Reader<'r> { - /// Unwraps the result of `from_slice(..)` with confidence and we assume that it's impossible to fail. - fn from_slice_should_be_ok(slice: &'r [u8]) -> Self; +/// Trait for obtaining an advanced builder for `BlockView`. +pub trait AsBlockBuilder { + /// Creates a new advanced builder for `BlockView`. + fn new_advanced_builder() -> BlockBuilder; - /// Unwraps the result of `from_compatible_slice(..)` with confidence and we assume that it's impossible to fail. - fn from_compatible_slice_should_be_ok(slice: &'r [u8]) -> Self; + /// Gets an advanced builder from the implementing type. + fn as_advanced_builder(&self) -> BlockBuilder; } -impl<'r, R> FromSliceShouldBeOk<'r> for R -where - R: Reader<'r>, -{ - fn from_slice_should_be_ok(slice: &'r [u8]) -> Self { - match Self::from_slice(slice) { - Ok(ret) => ret, - Err(err) => panic!( - "failed to convert from slice: reason: {}; data: 0x{}.", - err, - hex_string(slice) - ), - } - } - - fn from_compatible_slice_should_be_ok(slice: &'r [u8]) -> Self { - match Self::from_compatible_slice(slice) { - Ok(ret) => ret, - Err(err) => panic!( - "failed to convert from slice: reason: {}; data: 0x{}.", - err, - hex_string(slice) - ), - } - } +/// Trait for obtaining an advanced builder for `TransactionView`. +pub trait AsTransactionBuilder { + /// Gets an advanced builder for `TransactionView` from the implementing type. + fn as_advanced_builder(&self) -> TransactionBuilder; } -/// A syntactic sugar to convert binary data into rust types. -pub trait Unpack { - /// Unpack binary data into rust types. - fn unpack(&self) -> T; +/// Trait for obtaining an advanced builder for `HeaderView`. +pub trait AsHeaderBuilder { + /// Gets an advanced builder for `HeaderView` from the implementing type. + fn as_advanced_builder(&self) -> HeaderBuilder; } -/// A syntactic sugar to convert a rust type into binary data. -pub trait Pack { - /// Packs a rust type into binary data. - fn pack(&self) -> T; +/// Trait for calculating difficulty. +pub trait Difficulty { + /// Calculates and returns the difficulty value as a `U256`. + fn difficulty(&self) -> U256; +} + +/// Trait for building a compact block from a `BlockView`. +pub trait BuildCompactBlock { + /// Builds a compact block from a `BlockView` and a set of prefilled transaction indexes. + fn build_from_block( + block: &BlockView, + prefilled_transactions_indexes: &HashSet, + ) -> packed::CompactBlock; + + /// Returns the short IDs of the transactions in the compact block. + fn block_short_ids(&self) -> Vec>; + + /// Returns the indexes of the short IDs in the compact block. + fn short_id_indexes(&self) -> Vec; +} + +/// Trait for resetting the header of a packed block. +pub trait ResetBlock { + /// Resets the header of the packed block. + fn reset_header(self) -> packed::Block; + + /// Resets the header of the packed block with given transaction hashes and witness hashes. + fn reset_header_with_hashes( + self, + tx_hashes: &[packed::Byte32], + tx_witness_hashes: &[packed::Byte32], + ) -> packed::Block; } -/// A syntactic sugar to convert a vector of binary data into one binary data. -pub trait PackVec: IntoIterator { - /// Packs a vector of binary data into one binary data. - fn pack(self) -> T; +/// Trait for calculating the extra hash of a block. +pub trait CalcExtraHash { + /// Calculates and returns the extra hash of the block as an `ExtraHashView`. + fn calc_extra_hash(&self) -> ExtraHashView; } diff --git a/util/types/src/utilities/merkle_mountain_range.rs b/util/types/src/utilities/merkle_mountain_range.rs index 83893e9e3b..a6a58ff230 100644 --- a/util/types/src/utilities/merkle_mountain_range.rs +++ b/util/types/src/utilities/merkle_mountain_range.rs @@ -58,14 +58,15 @@ impl core::HeaderView { } } -impl packed::HeaderDigest { - fn is_default(&self) -> bool { - let default = Self::default(); - self.as_slice() == default.as_slice() - } +/// Trait for representing a header digest. +pub trait HeaderDigest { + /// Verify the header digest + fn verify(&self) -> Result<(), String>; +} +impl HeaderDigest for packed::HeaderDigest { /// Verify the MMR header digest - pub fn verify(&self) -> Result<(), String> { + fn verify(&self) -> Result<(), String> { // 1. Check block numbers. let start_number: BlockNumber = self.start_number().unpack(); let end_number: BlockNumber = self.end_number().unpack();