From 1a7bfad196c4520bced9bd40042beb9903c8d986 Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 11 Oct 2023 14:27:38 +0900 Subject: [PATCH] feat: tweak `SendBlocksProof` message to support ckb2023 --- util/gen-types/schemas/blockchain.mol | 2 +- util/gen-types/schemas/extensions.mol | 19 + util/gen-types/src/extension/shortcut.rs | 8 + util/gen-types/src/generated/blockchain.rs | 340 +++++++++++++++ util/gen-types/src/generated/extensions.rs | 396 ++++++++++++++++++ .../src/components/get_blocks_proof.rs | 52 ++- .../src/utilities/merkle_mountain_range.rs | 19 + 7 files changed, 824 insertions(+), 12 deletions(-) diff --git a/util/gen-types/schemas/blockchain.mol b/util/gen-types/schemas/blockchain.mol index 3ab343d856..c6c9b03d3a 100644 --- a/util/gen-types/schemas/blockchain.mol +++ b/util/gen-types/schemas/blockchain.mol @@ -10,7 +10,7 @@ array Uint256 [byte; 32]; vector Bytes ; option BytesOpt (Bytes); - +vector BytesOptVec ; vector BytesVec ; vector Byte32Vec ; diff --git a/util/gen-types/schemas/extensions.mol b/util/gen-types/schemas/extensions.mol index 8a58212200..14f5741122 100644 --- a/util/gen-types/schemas/extensions.mol +++ b/util/gen-types/schemas/extensions.mol @@ -375,6 +375,25 @@ table SendBlocksProof { missing_block_hashes: Byte32Vec, } +table SendBlocksProofV1 { + // Refer to `SendLastStateProof.last_header`. + last_header: VerifiableHeader, + // Refer to `SendLastStateProof.proof`. + proof: HeaderDigestVec, + + // Block headers for the blocks which require verifying. + headers: HeaderVec, + + // Block hashes for the blocks which were not found. + missing_block_hashes: Byte32Vec, + + // Uncle hashes for the blocks which require verifying. + blocks_uncles_hash: Byte32Vec, + + // Block extension for the blocks which require verifying. + blocks_extension: BytesOptVec, +} + table GetTransactionsProof { // Refer to `GetLastStateProof.last_hash`. last_hash: Byte32, diff --git a/util/gen-types/src/extension/shortcut.rs b/util/gen-types/src/extension/shortcut.rs index 84bfe9e0af..965c2e8320 100644 --- a/util/gen-types/src/extension/shortcut.rs +++ b/util/gen-types/src/extension/shortcut.rs @@ -295,3 +295,11 @@ impl packed::HeaderDigest { self.as_slice() == default.as_slice() } } + +impl From for packed::LightClientMessageUnion { + fn from(item: packed::SendBlocksProofV1) -> Self { + packed::LightClientMessageUnion::SendBlocksProof(packed::SendBlocksProof::new_unchecked( + item.as_bytes(), + )) + } +} diff --git a/util/gen-types/src/generated/blockchain.rs b/util/gen-types/src/generated/blockchain.rs index c7f33186fa..3eb5b9b6b9 100644 --- a/util/gen-types/src/generated/blockchain.rs +++ b/util/gen-types/src/generated/blockchain.rs @@ -2386,6 +2386,346 @@ impl molecule::prelude::Builder for BytesOptBuilder { } } #[derive(Clone)] +pub struct BytesOptVec(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for BytesOptVec { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for BytesOptVec { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for BytesOptVec { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + for i in 0..self.len() { + if i == 0 { + write!(f, "{}", self.get_unchecked(i))?; + } else { + write!(f, ", {}", self.get_unchecked(i))?; + } + } + write!(f, "]") + } +} +impl ::core::default::Default for BytesOptVec { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + BytesOptVec::new_unchecked(v) + } +} +impl BytesOptVec { + const DEFAULT_VALUE: [u8; 4] = [4, 0, 0, 0]; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn item_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn len(&self) -> usize { + self.item_count() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn get(&self, idx: usize) -> Option { + if idx >= self.len() { + None + } else { + Some(self.get_unchecked(idx)) + } + } + pub fn get_unchecked(&self, idx: usize) -> BytesOpt { + let slice = self.as_slice(); + let start_idx = molecule::NUMBER_SIZE * (1 + idx); + let start = molecule::unpack_number(&slice[start_idx..]) as usize; + if idx == self.len() - 1 { + BytesOpt::new_unchecked(self.0.slice(start..)) + } else { + let end_idx = start_idx + molecule::NUMBER_SIZE; + let end = molecule::unpack_number(&slice[end_idx..]) as usize; + BytesOpt::new_unchecked(self.0.slice(start..end)) + } + } + pub fn as_reader<'r>(&'r self) -> BytesOptVecReader<'r> { + BytesOptVecReader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for BytesOptVec { + type Builder = BytesOptVecBuilder; + const NAME: &'static str = "BytesOptVec"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + BytesOptVec(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BytesOptVecReader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + BytesOptVecReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder().extend(self.into_iter()) + } +} +#[derive(Clone, Copy)] +pub struct BytesOptVecReader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for BytesOptVecReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for BytesOptVecReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for BytesOptVecReader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} [", Self::NAME)?; + for i in 0..self.len() { + if i == 0 { + write!(f, "{}", self.get_unchecked(i))?; + } else { + write!(f, ", {}", self.get_unchecked(i))?; + } + } + write!(f, "]") + } +} +impl<'r> BytesOptVecReader<'r> { + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn item_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn len(&self) -> usize { + self.item_count() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn get(&self, idx: usize) -> Option> { + if idx >= self.len() { + None + } else { + Some(self.get_unchecked(idx)) + } + } + pub fn get_unchecked(&self, idx: usize) -> BytesOptReader<'r> { + let slice = self.as_slice(); + let start_idx = molecule::NUMBER_SIZE * (1 + idx); + let start = molecule::unpack_number(&slice[start_idx..]) as usize; + if idx == self.len() - 1 { + BytesOptReader::new_unchecked(&self.as_slice()[start..]) + } else { + let end_idx = start_idx + molecule::NUMBER_SIZE; + let end = molecule::unpack_number(&slice[end_idx..]) as usize; + BytesOptReader::new_unchecked(&self.as_slice()[start..end]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for BytesOptVecReader<'r> { + type Entity = BytesOptVec; + const NAME: &'static str = "BytesOptVecReader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + BytesOptVecReader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len == molecule::NUMBER_SIZE { + return Ok(()); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!( + Self, + TotalSizeNotMatch, + molecule::NUMBER_SIZE * 2, + slice_len + ); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + for pair in offsets.windows(2) { + let start = pair[0]; + let end = pair[1]; + BytesOptReader::verify(&slice[start..end], compatible)?; + } + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct BytesOptVecBuilder(pub(crate) Vec); +impl BytesOptVecBuilder { + pub fn set(mut self, v: Vec) -> Self { + self.0 = v; + self + } + pub fn push(mut self, v: BytesOpt) -> Self { + self.0.push(v); + self + } + pub fn extend>(mut self, iter: T) -> Self { + for elem in iter { + self.0.push(elem); + } + self + } + pub fn replace(&mut self, index: usize, v: BytesOpt) -> Option { + self.0 + .get_mut(index) + .map(|item| ::core::mem::replace(item, v)) + } +} +impl molecule::prelude::Builder for BytesOptVecBuilder { + type Entity = BytesOptVec; + const NAME: &'static str = "BytesOptVecBuilder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (self.0.len() + 1) + + self + .0 + .iter() + .map(|inner| inner.as_slice().len()) + .sum::() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let item_count = self.0.len(); + if item_count == 0 { + writer.write_all(&molecule::pack_number( + molecule::NUMBER_SIZE as molecule::Number, + ))?; + } else { + let (total_size, offsets) = self.0.iter().fold( + ( + molecule::NUMBER_SIZE * (item_count + 1), + Vec::with_capacity(item_count), + ), + |(start, mut offsets), inner| { + offsets.push(start); + (start + inner.as_slice().len(), offsets) + }, + ); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + for inner in self.0.iter() { + writer.write_all(inner.as_slice())?; + } + } + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + BytesOptVec::new_unchecked(inner.into()) + } +} +pub struct BytesOptVecIterator(BytesOptVec, usize, usize); +impl ::core::iter::Iterator for BytesOptVecIterator { + type Item = BytesOpt; + fn next(&mut self) -> Option { + if self.1 >= self.2 { + None + } else { + let ret = self.0.get_unchecked(self.1); + self.1 += 1; + Some(ret) + } + } +} +impl ::core::iter::ExactSizeIterator for BytesOptVecIterator { + fn len(&self) -> usize { + self.2 - self.1 + } +} +impl ::core::iter::IntoIterator for BytesOptVec { + type Item = BytesOpt; + type IntoIter = BytesOptVecIterator; + fn into_iter(self) -> Self::IntoIter { + let len = self.len(); + BytesOptVecIterator(self, 0, len) + } +} +impl<'r> BytesOptVecReader<'r> { + pub fn iter<'t>(&'t self) -> BytesOptVecReaderIterator<'t, 'r> { + BytesOptVecReaderIterator(&self, 0, self.len()) + } +} +pub struct BytesOptVecReaderIterator<'t, 'r>(&'t BytesOptVecReader<'r>, usize, usize); +impl<'t: 'r, 'r> ::core::iter::Iterator for BytesOptVecReaderIterator<'t, 'r> { + type Item = BytesOptReader<'t>; + fn next(&mut self) -> Option { + if self.1 >= self.2 { + None + } else { + let ret = self.0.get_unchecked(self.1); + self.1 += 1; + Some(ret) + } + } +} +impl<'t: 'r, 'r> ::core::iter::ExactSizeIterator for BytesOptVecReaderIterator<'t, 'r> { + fn len(&self) -> usize { + self.2 - self.1 + } +} +#[derive(Clone)] pub struct BytesVec(molecule::bytes::Bytes); impl ::core::fmt::LowerHex for BytesVec { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { diff --git a/util/gen-types/src/generated/extensions.rs b/util/gen-types/src/generated/extensions.rs index 7c7bcd0729..7d96076678 100644 --- a/util/gen-types/src/generated/extensions.rs +++ b/util/gen-types/src/generated/extensions.rs @@ -17568,6 +17568,402 @@ impl molecule::prelude::Builder for SendBlocksProofBuilder { } } #[derive(Clone)] +pub struct SendBlocksProofV1(molecule::bytes::Bytes); +impl ::core::fmt::LowerHex for SendBlocksProofV1 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl ::core::fmt::Debug for SendBlocksProofV1 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl ::core::fmt::Display for SendBlocksProofV1 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "last_header", self.last_header())?; + write!(f, ", {}: {}", "proof", self.proof())?; + write!(f, ", {}: {}", "headers", self.headers())?; + write!( + f, + ", {}: {}", + "missing_block_hashes", + self.missing_block_hashes() + )?; + write!( + f, + ", {}: {}", + "blocks_uncles_hash", + self.blocks_uncles_hash() + )?; + write!(f, ", {}: {}", "blocks_extension", self.blocks_extension())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl ::core::default::Default for SendBlocksProofV1 { + fn default() -> Self { + let v = molecule::bytes::Bytes::from_static(&Self::DEFAULT_VALUE); + SendBlocksProofV1::new_unchecked(v) + } +} +impl SendBlocksProofV1 { + const DEFAULT_VALUE: [u8; 428] = [ + 172, 1, 0, 0, 28, 0, 0, 0, 152, 1, 0, 0, 156, 1, 0, 0, 160, 1, 0, 0, 164, 1, 0, 0, 168, 1, + 0, 0, 124, 1, 0, 0, 20, 0, 0, 0, 228, 0, 0, 0, 4, 1, 0, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, + ]; + pub const FIELD_COUNT: usize = 6; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn last_header(&self) -> VerifiableHeader { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + VerifiableHeader::new_unchecked(self.0.slice(start..end)) + } + pub fn proof(&self) -> HeaderDigestVec { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + HeaderDigestVec::new_unchecked(self.0.slice(start..end)) + } + pub fn headers(&self) -> HeaderVec { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + HeaderVec::new_unchecked(self.0.slice(start..end)) + } + pub fn missing_block_hashes(&self) -> Byte32Vec { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Byte32Vec::new_unchecked(self.0.slice(start..end)) + } + pub fn blocks_uncles_hash(&self) -> Byte32Vec { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32Vec::new_unchecked(self.0.slice(start..end)) + } + pub fn blocks_extension(&self) -> BytesOptVec { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[28..]) as usize; + BytesOptVec::new_unchecked(self.0.slice(start..end)) + } else { + BytesOptVec::new_unchecked(self.0.slice(start..)) + } + } + pub fn as_reader<'r>(&'r self) -> SendBlocksProofV1Reader<'r> { + SendBlocksProofV1Reader::new_unchecked(self.as_slice()) + } +} +impl molecule::prelude::Entity for SendBlocksProofV1 { + type Builder = SendBlocksProofV1Builder; + const NAME: &'static str = "SendBlocksProofV1"; + fn new_unchecked(data: molecule::bytes::Bytes) -> Self { + SendBlocksProofV1(data) + } + fn as_bytes(&self) -> molecule::bytes::Bytes { + self.0.clone() + } + fn as_slice(&self) -> &[u8] { + &self.0[..] + } + fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SendBlocksProofV1Reader::from_slice(slice).map(|reader| reader.to_entity()) + } + fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { + SendBlocksProofV1Reader::from_compatible_slice(slice).map(|reader| reader.to_entity()) + } + fn new_builder() -> Self::Builder { + ::core::default::Default::default() + } + fn as_builder(self) -> Self::Builder { + Self::new_builder() + .last_header(self.last_header()) + .proof(self.proof()) + .headers(self.headers()) + .missing_block_hashes(self.missing_block_hashes()) + .blocks_uncles_hash(self.blocks_uncles_hash()) + .blocks_extension(self.blocks_extension()) + } +} +#[derive(Clone, Copy)] +pub struct SendBlocksProofV1Reader<'r>(&'r [u8]); +impl<'r> ::core::fmt::LowerHex for SendBlocksProofV1Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use molecule::hex_string; + if f.alternate() { + write!(f, "0x")?; + } + write!(f, "{}", hex_string(self.as_slice())) + } +} +impl<'r> ::core::fmt::Debug for SendBlocksProofV1Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:#x})", Self::NAME, self) + } +} +impl<'r> ::core::fmt::Display for SendBlocksProofV1Reader<'r> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{} {{ ", Self::NAME)?; + write!(f, "{}: {}", "last_header", self.last_header())?; + write!(f, ", {}: {}", "proof", self.proof())?; + write!(f, ", {}: {}", "headers", self.headers())?; + write!( + f, + ", {}: {}", + "missing_block_hashes", + self.missing_block_hashes() + )?; + write!( + f, + ", {}: {}", + "blocks_uncles_hash", + self.blocks_uncles_hash() + )?; + write!(f, ", {}: {}", "blocks_extension", self.blocks_extension())?; + let extra_count = self.count_extra_fields(); + if extra_count != 0 { + write!(f, ", .. ({} fields)", extra_count)?; + } + write!(f, " }}") + } +} +impl<'r> SendBlocksProofV1Reader<'r> { + pub const FIELD_COUNT: usize = 6; + pub fn total_size(&self) -> usize { + molecule::unpack_number(self.as_slice()) as usize + } + pub fn field_count(&self) -> usize { + if self.total_size() == molecule::NUMBER_SIZE { + 0 + } else { + (molecule::unpack_number(&self.as_slice()[molecule::NUMBER_SIZE..]) as usize / 4) - 1 + } + } + pub fn count_extra_fields(&self) -> usize { + self.field_count() - Self::FIELD_COUNT + } + pub fn has_extra_fields(&self) -> bool { + Self::FIELD_COUNT != self.field_count() + } + pub fn last_header(&self) -> VerifiableHeaderReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[4..]) as usize; + let end = molecule::unpack_number(&slice[8..]) as usize; + VerifiableHeaderReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn proof(&self) -> HeaderDigestVecReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[8..]) as usize; + let end = molecule::unpack_number(&slice[12..]) as usize; + HeaderDigestVecReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn headers(&self) -> HeaderVecReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[12..]) as usize; + let end = molecule::unpack_number(&slice[16..]) as usize; + HeaderVecReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn missing_block_hashes(&self) -> Byte32VecReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[16..]) as usize; + let end = molecule::unpack_number(&slice[20..]) as usize; + Byte32VecReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn blocks_uncles_hash(&self) -> Byte32VecReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[20..]) as usize; + let end = molecule::unpack_number(&slice[24..]) as usize; + Byte32VecReader::new_unchecked(&self.as_slice()[start..end]) + } + pub fn blocks_extension(&self) -> BytesOptVecReader<'r> { + let slice = self.as_slice(); + let start = molecule::unpack_number(&slice[24..]) as usize; + if self.has_extra_fields() { + let end = molecule::unpack_number(&slice[28..]) as usize; + BytesOptVecReader::new_unchecked(&self.as_slice()[start..end]) + } else { + BytesOptVecReader::new_unchecked(&self.as_slice()[start..]) + } + } +} +impl<'r> molecule::prelude::Reader<'r> for SendBlocksProofV1Reader<'r> { + type Entity = SendBlocksProofV1; + const NAME: &'static str = "SendBlocksProofV1Reader"; + fn to_entity(&self) -> Self::Entity { + Self::Entity::new_unchecked(self.as_slice().to_owned().into()) + } + fn new_unchecked(slice: &'r [u8]) -> Self { + SendBlocksProofV1Reader(slice) + } + fn as_slice(&self) -> &'r [u8] { + self.0 + } + fn verify(slice: &[u8], compatible: bool) -> molecule::error::VerificationResult<()> { + use molecule::verification_error as ve; + let slice_len = slice.len(); + if slice_len < molecule::NUMBER_SIZE { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE, slice_len); + } + let total_size = molecule::unpack_number(slice) as usize; + if slice_len != total_size { + return ve!(Self, TotalSizeNotMatch, total_size, slice_len); + } + if slice_len < molecule::NUMBER_SIZE * 2 { + return ve!(Self, HeaderIsBroken, molecule::NUMBER_SIZE * 2, slice_len); + } + let offset_first = molecule::unpack_number(&slice[molecule::NUMBER_SIZE..]) as usize; + if offset_first % molecule::NUMBER_SIZE != 0 || offset_first < molecule::NUMBER_SIZE * 2 { + return ve!(Self, OffsetsNotMatch); + } + if slice_len < offset_first { + return ve!(Self, HeaderIsBroken, offset_first, slice_len); + } + let field_count = offset_first / molecule::NUMBER_SIZE - 1; + if field_count < Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + } else if !compatible && field_count > Self::FIELD_COUNT { + return ve!(Self, FieldCountNotMatch, Self::FIELD_COUNT, field_count); + }; + let mut offsets: Vec = slice[molecule::NUMBER_SIZE..offset_first] + .chunks_exact(molecule::NUMBER_SIZE) + .map(|x| molecule::unpack_number(x) as usize) + .collect(); + offsets.push(total_size); + if offsets.windows(2).any(|i| i[0] > i[1]) { + return ve!(Self, OffsetsNotMatch); + } + VerifiableHeaderReader::verify(&slice[offsets[0]..offsets[1]], compatible)?; + HeaderDigestVecReader::verify(&slice[offsets[1]..offsets[2]], compatible)?; + HeaderVecReader::verify(&slice[offsets[2]..offsets[3]], compatible)?; + Byte32VecReader::verify(&slice[offsets[3]..offsets[4]], compatible)?; + Byte32VecReader::verify(&slice[offsets[4]..offsets[5]], compatible)?; + BytesOptVecReader::verify(&slice[offsets[5]..offsets[6]], compatible)?; + Ok(()) + } +} +#[derive(Debug, Default)] +pub struct SendBlocksProofV1Builder { + pub(crate) last_header: VerifiableHeader, + pub(crate) proof: HeaderDigestVec, + pub(crate) headers: HeaderVec, + pub(crate) missing_block_hashes: Byte32Vec, + pub(crate) blocks_uncles_hash: Byte32Vec, + pub(crate) blocks_extension: BytesOptVec, +} +impl SendBlocksProofV1Builder { + pub const FIELD_COUNT: usize = 6; + pub fn last_header(mut self, v: VerifiableHeader) -> Self { + self.last_header = v; + self + } + pub fn proof(mut self, v: HeaderDigestVec) -> Self { + self.proof = v; + self + } + pub fn headers(mut self, v: HeaderVec) -> Self { + self.headers = v; + self + } + pub fn missing_block_hashes(mut self, v: Byte32Vec) -> Self { + self.missing_block_hashes = v; + self + } + pub fn blocks_uncles_hash(mut self, v: Byte32Vec) -> Self { + self.blocks_uncles_hash = v; + self + } + pub fn blocks_extension(mut self, v: BytesOptVec) -> Self { + self.blocks_extension = v; + self + } +} +impl molecule::prelude::Builder for SendBlocksProofV1Builder { + type Entity = SendBlocksProofV1; + const NAME: &'static str = "SendBlocksProofV1Builder"; + fn expected_length(&self) -> usize { + molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1) + + self.last_header.as_slice().len() + + self.proof.as_slice().len() + + self.headers.as_slice().len() + + self.missing_block_hashes.as_slice().len() + + self.blocks_uncles_hash.as_slice().len() + + self.blocks_extension.as_slice().len() + } + fn write(&self, writer: &mut W) -> molecule::io::Result<()> { + let mut total_size = molecule::NUMBER_SIZE * (Self::FIELD_COUNT + 1); + let mut offsets = Vec::with_capacity(Self::FIELD_COUNT); + offsets.push(total_size); + total_size += self.last_header.as_slice().len(); + offsets.push(total_size); + total_size += self.proof.as_slice().len(); + offsets.push(total_size); + total_size += self.headers.as_slice().len(); + offsets.push(total_size); + total_size += self.missing_block_hashes.as_slice().len(); + offsets.push(total_size); + total_size += self.blocks_uncles_hash.as_slice().len(); + offsets.push(total_size); + total_size += self.blocks_extension.as_slice().len(); + writer.write_all(&molecule::pack_number(total_size as molecule::Number))?; + for offset in offsets.into_iter() { + writer.write_all(&molecule::pack_number(offset as molecule::Number))?; + } + writer.write_all(self.last_header.as_slice())?; + writer.write_all(self.proof.as_slice())?; + writer.write_all(self.headers.as_slice())?; + writer.write_all(self.missing_block_hashes.as_slice())?; + writer.write_all(self.blocks_uncles_hash.as_slice())?; + writer.write_all(self.blocks_extension.as_slice())?; + Ok(()) + } + fn build(&self) -> Self::Entity { + let mut inner = Vec::with_capacity(self.expected_length()); + self.write(&mut inner) + .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); + SendBlocksProofV1::new_unchecked(inner.into()) + } +} +#[derive(Clone)] pub struct GetTransactionsProof(molecule::bytes::Bytes); impl ::core::fmt::LowerHex for GetTransactionsProof { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { diff --git a/util/light-client-protocol-server/src/components/get_blocks_proof.rs b/util/light-client-protocol-server/src/components/get_blocks_proof.rs index dd8e0ae174..991852309c 100644 --- a/util/light-client-protocol-server/src/components/get_blocks_proof.rs +++ b/util/light-client-protocol-server/src/components/get_blocks_proof.rs @@ -73,6 +73,9 @@ impl<'a> GetBlocksProofProcess<'a> { let mut positions = Vec::with_capacity(found.len()); let mut block_headers = Vec::with_capacity(found.len()); + let mut uncles_hash = Vec::with_capacity(found.len()); + let mut extensions = Vec::with_capacity(found.len()); + let ckb2023 = self.nc.ckb2023(); for block_hash in found { let header = snapshot @@ -80,18 +83,45 @@ impl<'a> GetBlocksProofProcess<'a> { .expect("header should be in store"); positions.push(leaf_index_to_pos(header.number())); block_headers.push(header.data()); + if ckb2023 { + let uncles = snapshot + .get_block_uncles(&block_hash) + .expect("block uncles must be stored"); + let extension = snapshot.get_block_extension(&block_hash); + + uncles_hash.push(uncles.data().calc_uncles_hash()); + extensions.push(packed::BytesOpt::new_builder().set(extension).build()); + } } - let proved_items = block_headers.pack(); - let missing_items = missing.pack(); - - self.protocol.reply_proof::( - self.peer, - self.nc, - &last_block, - positions, - proved_items, - missing_items, - ) + if ckb2023 { + let proved_items = ( + block_headers.pack(), + uncles_hash.pack(), + packed::BytesOptVec::new_builder().set(extensions).build(), + ); + let missing_items = missing.pack(); + + self.protocol.reply_proof::( + self.peer, + self.nc, + &last_block, + positions, + proved_items, + missing_items, + ) + } else { + let proved_items = block_headers.pack(); + let missing_items = missing.pack(); + + self.protocol.reply_proof::( + self.peer, + self.nc, + &last_block, + positions, + proved_items, + missing_items, + ) + } } } diff --git a/util/types/src/utilities/merkle_mountain_range.rs b/util/types/src/utilities/merkle_mountain_range.rs index a6a58ff230..c095ec0c70 100644 --- a/util/types/src/utilities/merkle_mountain_range.rs +++ b/util/types/src/utilities/merkle_mountain_range.rs @@ -321,6 +321,25 @@ impl ProverMessageBuilder for packed::SendBlocksProofBuilder { } } +impl ProverMessageBuilder for packed::SendBlocksProofV1Builder { + type ProvedItems = (packed::HeaderVec, packed::Byte32Vec, packed::BytesOptVec); + type MissingItems = packed::Byte32Vec; + fn set_last_header(self, last_header: packed::VerifiableHeader) -> Self { + self.last_header(last_header) + } + fn set_proof(self, proof: packed::HeaderDigestVec) -> Self { + self.proof(proof) + } + fn set_proved_items(self, items: Self::ProvedItems) -> Self { + self.headers(items.0) + .blocks_uncles_hash(items.1) + .blocks_extension(items.2) + } + fn set_missing_items(self, items: Self::MissingItems) -> Self { + self.missing_block_hashes(items) + } +} + impl ProverMessageBuilder for packed::SendTransactionsProofBuilder { type ProvedItems = packed::FilteredBlockVec; type MissingItems = packed::Byte32Vec;