Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into upstream-0.17.1
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaranov34 committed Aug 7, 2024
2 parents 488b054 + b96a525 commit 68e157f
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 2 deletions.
187 changes: 186 additions & 1 deletion src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use crate::types::{TonHash, DEFAULT_CELL_HASH};
mod bag_of_cells;
mod bit_string;
mod builder;

mod cell_type;
mod dict_loader;
mod error;
Expand Down Expand Up @@ -617,3 +616,189 @@ mod test {
assert_eq!(result, expected)
}
}

fn get_repr_for_data(
(original_data, original_data_bit_len): (&[u8], usize),
(data, data_bit_len): (&[u8], usize),
refs: &[ArcCell],
level_mask: LevelMask,
level: u8,
cell_type: CellType,
) -> Result<Vec<u8>, TonCellError> {
// Allocate
let data_len = data.len();
// descriptors + data + (hash + depth) * refs_count
let buffer_len = 2 + data_len + (32 + 2) * refs.len();

let mut writer = BitWriter::endian(Vec::with_capacity(buffer_len), BigEndian);
let d1 = get_refs_descriptor(cell_type, refs, level_mask.apply(level).mask());
let d2 = get_bits_descriptor(original_data, original_data_bit_len);

// Write descriptors
writer.write(8, d1).map_cell_parser_error()?;
writer.write(8, d2).map_cell_parser_error()?;
// Write main data
write_data(&mut writer, data, data_bit_len).map_cell_parser_error()?;
// Write ref data
write_ref_depths(&mut writer, refs, cell_type, level)?;
write_ref_hashes(&mut writer, refs, cell_type, level)?;

let result = writer
.writer()
.ok_or_else(|| TonCellError::cell_builder_error("Stream for cell repr is not byte-aligned"))
.map(|b| b.to_vec());

result
}

/// This function replicates unknown logic of resolving cell data
/// https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214
fn calculate_hashes_and_depths(
cell_type: CellType,
data: &[u8],
bit_len: usize,
references: &[ArcCell],
level_mask: LevelMask,
) -> Result<([CellHash; 4], [u16; 4]), TonCellError> {
let hash_count = if cell_type == CellType::PrunedBranch {
1
} else {
level_mask.hash_count()
};

let total_hash_count = level_mask.hash_count();
let hash_i_offset = total_hash_count - hash_count;

let mut depths: Vec<u16> = Vec::with_capacity(hash_count);
let mut hashes: Vec<CellHash> = Vec::with_capacity(hash_count);

// Iterate through significant levels
for (hash_i, level_i) in (0..=level_mask.level())
.filter(|&i| level_mask.is_significant(i))
.enumerate()
{
if hash_i < hash_i_offset {
continue;
}

let (current_data, current_bit_len) = if hash_i == hash_i_offset {
(data, bit_len)
} else {
let previous_hash = hashes
.get(hash_i - hash_i_offset - 1)
.ok_or_else(|| TonCellError::InternalError("Can't get right hash".to_owned()))?;
(previous_hash.as_slice(), 256)
};

// Calculate Depth
let depth = if references.is_empty() {
0
} else {
let max_ref_depth = references.iter().fold(0, |max_depth, reference| {
let child_depth = cell_type.child_depth(reference, level_i);
max_depth.max(child_depth)
});

max_ref_depth + 1
};

// Calculate Hash
let repr = get_repr_for_data(
(data, bit_len),
(current_data, current_bit_len),
references,
level_mask,
level_i,
cell_type,
)?;
let hash = Sha256::new_with_prefix(repr).finalize()[..]
.try_into()
.map_err(|error| {
TonCellError::InternalError(format!(
"Can't get [u8; 32] from finalized hash with error: {error}"
))
})?;

depths.push(depth);
hashes.push(hash);
}

cell_type.resolve_hashes_and_depths(hashes, depths, data, bit_len, level_mask)
}

fn get_refs_descriptor(cell_type: CellType, references: &[ArcCell], level_mask: u32) -> u8 {
let cell_type_var = (cell_type != CellType::Ordinary) as u8;
references.len() as u8 + 8 * cell_type_var + level_mask as u8 * 32
}

fn get_bits_descriptor(data: &[u8], bit_len: usize) -> u8 {
let rest_bits = bit_len % 8;
let full_bytes = rest_bits == 0;
data.len() as u8 * 2 - !full_bytes as u8 // subtract 1 if the last byte is not full
}

fn write_data(
writer: &mut BitWriter<Vec<u8>, BigEndian>,
data: &[u8],
bit_len: usize,
) -> Result<(), io::Error> {
let data_len = data.len();
let rest_bits = bit_len % 8;
let full_bytes = rest_bits == 0;

if !full_bytes {
writer.write_bytes(&data[..data_len - 1])?;
let last_byte = data[data_len - 1];
let l = last_byte | 1 << (8 - rest_bits - 1);
writer.write(8, l)?;
} else {
writer.write_bytes(data)?;
}

Ok(())
}

fn write_ref_depths(
writer: &mut BitWriter<Vec<u8>, BigEndian>,
refs: &[ArcCell],
parent_cell_type: CellType,
level: u8,
) -> Result<(), TonCellError> {
for reference in refs {
let child_depth = if matches!(
parent_cell_type,
CellType::MerkleProof | CellType::MerkleUpdate
) {
reference.get_depth(level + 1)
} else {
reference.get_depth(level)
};

writer.write(8, child_depth / 256).map_cell_parser_error()?;
writer.write(8, child_depth % 256).map_cell_parser_error()?;
}

Ok(())
}

fn write_ref_hashes(
writer: &mut BitWriter<Vec<u8>, BigEndian>,
refs: &[ArcCell],
parent_cell_type: CellType,
level: u8,
) -> Result<(), TonCellError> {
for reference in refs {
let child_hash = if matches!(
parent_cell_type,
CellType::MerkleProof | CellType::MerkleUpdate
) {
reference.get_hash(level + 1)
} else {
reference.get_hash(level)
};

writer.write_bytes(&child_hash).map_cell_parser_error()?;
}

Ok(())
}
2 changes: 1 addition & 1 deletion src/cell/state_init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ArcCell;
use super::{ArcCell, CellHash};
use crate::cell::{Cell, CellBuilder, TonCellError};
use crate::types::TonHash;

Expand Down

0 comments on commit 68e157f

Please sign in to comment.