use std::collections::HashSet;
use crate::{
bytes,
core::{self, BlockNumber},
packed,
prelude::*,
utilities::{compact_to_difficulty, merkle_root},
U256,
};
impl packed::Byte32 {
pub fn zero() -> Self {
Self::default()
}
pub fn max_value() -> Self {
[u8::max_value(); 32].pack()
}
pub fn is_zero(&self) -> bool {
self.as_slice().iter().all(|x| *x == 0)
}
pub fn new(v: [u8; 32]) -> Self {
v.pack()
}
}
impl packed::ProposalShortId {
pub fn from_tx_hash(h: &packed::Byte32) -> Self {
let mut inner = [0u8; 10];
inner.copy_from_slice(&h.as_slice()[..10]);
inner.pack()
}
pub fn zero() -> Self {
Self::default()
}
pub fn new(v: [u8; 10]) -> Self {
v.pack()
}
}
impl packed::OutPoint {
pub fn new(tx_hash: packed::Byte32, index: u32) -> Self {
packed::OutPoint::new_builder()
.tx_hash(tx_hash)
.index(index.pack())
.build()
}
pub fn null() -> Self {
packed::OutPoint::new_builder()
.index(u32::max_value().pack())
.build()
}
pub fn is_null(&self) -> bool {
self.tx_hash().is_zero() && Unpack::<u32>::unpack(&self.index()) == u32::max_value()
}
pub fn to_cell_key(&self) -> Vec<u8> {
let mut key = Vec::with_capacity(36);
let index: u32 = self.index().unpack();
key.extend_from_slice(self.tx_hash().as_slice());
key.extend_from_slice(&index.to_be_bytes());
key
}
}
impl packed::CellInput {
pub fn new(previous_output: packed::OutPoint, block_number: BlockNumber) -> Self {
packed::CellInput::new_builder()
.since(block_number.pack())
.previous_output(previous_output)
.build()
}
pub fn new_cellbase_input(block_number: BlockNumber) -> Self {
Self::new(packed::OutPoint::null(), block_number)
}
}
impl packed::Script {
pub fn into_witness(self) -> packed::Bytes {
packed::CellbaseWitness::new_builder()
.lock(self)
.build()
.as_bytes()
.pack()
}
pub fn from_witness(witness: packed::Bytes) -> Option<Self> {
packed::CellbaseWitness::from_slice(&witness.raw_data())
.map(|cellbase_witness| cellbase_witness.lock())
.ok()
}
pub fn is_hash_type_type(&self) -> bool {
Into::<u8>::into(self.hash_type()) == Into::<u8>::into(core::ScriptHashType::Type)
}
}
impl packed::Transaction {
pub fn is_cellbase(&self) -> bool {
let raw_tx = self.raw();
raw_tx.inputs().len() == 1
&& self.witnesses().len() == 1
&& raw_tx
.inputs()
.get(0)
.should_be_ok()
.previous_output()
.is_null()
}
pub fn proposal_short_id(&self) -> packed::ProposalShortId {
packed::ProposalShortId::from_tx_hash(&self.calc_tx_hash())
}
}
impl packed::RawHeader {
pub fn difficulty(&self) -> U256 {
compact_to_difficulty(self.compact_target().unpack())
}
}
impl packed::Header {
pub fn difficulty(&self) -> U256 {
self.raw().difficulty()
}
}
impl packed::Block {
pub fn as_uncle(&self) -> packed::UncleBlock {
packed::UncleBlock::new_builder()
.header(self.header())
.proposals(self.proposals())
.build()
}
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()
}
}
pub fn extra_field(&self, index: usize) -> Option<bytes::Bytes> {
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..))
} else {
let j = i + molecule::NUMBER_SIZE;
let end = molecule::unpack_number(&slice[j..]) as usize;
Some(self.as_bytes().slice(start..end))
}
} else {
None
}
}
pub fn extension(&self) -> Option<packed::Bytes> {
self.extra_field(0)
.map(|data| packed::Bytes::from_slice(&data).unwrap())
}
}
impl packed::BlockV1 {
pub fn as_v0(&self) -> packed::Block {
packed::Block::new_unchecked(self.as_bytes())
}
}
impl<'r> packed::BlockReader<'r> {
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_slice()[start..])
} else {
let j = i + molecule::NUMBER_SIZE;
let end = molecule::unpack_number(&slice[j..]) as usize;
Some(&self.as_slice()[start..end])
}
} else {
None
}
}
pub fn extension(&self) -> Option<packed::BytesReader> {
self.extra_field(0)
.map(|data| packed::BytesReader::from_slice(data).unwrap())
}
}
impl<'r> packed::BlockV1Reader<'r> {
pub fn as_v0(&self) -> packed::BlockReader {
packed::BlockReader::new_unchecked(self.as_slice())
}
}
impl packed::CompactBlock {
pub fn build_from_block(
block: &core::BlockView,
prefilled_transactions_indexes: &HashSet<usize>,
) -> Self {
let prefilled_transactions_len = prefilled_transactions_indexes.len() + 1;
let mut short_ids: Vec<packed::ProposalShortId> = 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()
}
}
pub fn block_short_ids(&self) -> Vec<Option<packed::ProposalShortId>> {
let txs_len = self.txs_len();
let mut block_short_ids: Vec<Option<packed::ProposalShortId>> = Vec::with_capacity(txs_len);
let prefilled_indexes = self
.prefilled_transactions()
.into_iter()
.map(|tx_index| tx_index.index().unpack())
.collect::<HashSet<usize>>();
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
}
pub fn txs_len(&self) -> usize {
self.prefilled_transactions().len() + self.short_ids().len()
}
fn prefilled_indexes_iter(&self) -> impl Iterator<Item = usize> {
self.prefilled_transactions()
.into_iter()
.map(|i| i.index().unpack())
}
pub fn short_id_indexes(&self) -> Vec<usize> {
let prefilled_indexes: HashSet<usize> = self.prefilled_indexes_iter().collect();
(0..self.txs_len())
.filter(|index| !prefilled_indexes.contains(index))
.collect()
}
pub fn extra_field(&self, index: usize) -> Option<bytes::Bytes> {
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..))
} else {
let j = i + molecule::NUMBER_SIZE;
let end = molecule::unpack_number(&slice[j..]) as usize;
Some(self.as_bytes().slice(start..end))
}
} else {
None
}
}
pub fn extension(&self) -> Option<packed::Bytes> {
self.extra_field(0)
.map(|data| packed::Bytes::from_slice(&data).unwrap())
}
}
impl packed::CompactBlockV1 {
pub fn as_v0(&self) -> packed::CompactBlock {
packed::CompactBlock::new_unchecked(self.as_bytes())
}
}
impl<'r> packed::CompactBlockV1Reader<'r> {
pub fn as_v0(&self) -> packed::CompactBlockReader {
packed::CompactBlockReader::new_unchecked(self.as_slice())
}
}
impl AsRef<[u8]> for packed::TransactionKey {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}