mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
59
crypto/vm/cells/Cell.cpp
Normal file
59
crypto/vm/cells/Cell.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/Cell.h"
|
||||
#include "vm/cells/VirtualCell.h"
|
||||
#include "vm/cells/DataCell.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace vm {
|
||||
td::Status Cell::check_equals_unloaded(const Ref<Cell>& other) const {
|
||||
auto level_mask = get_level_mask();
|
||||
if (level_mask != other->get_level_mask()) {
|
||||
return td::Status::Error("level mismatch");
|
||||
}
|
||||
auto level = level_mask.get_level();
|
||||
for (unsigned i = 0; i <= level; i++) {
|
||||
if (!get_level_mask().is_significant(i)) {
|
||||
continue;
|
||||
}
|
||||
if (get_hash(i) != other->get_hash(i)) {
|
||||
return td::Status::Error("hash mismatch");
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i <= level; i++) {
|
||||
if (!get_level_mask().is_significant(i)) {
|
||||
continue;
|
||||
}
|
||||
if (get_depth(i) != other->get_depth(i)) {
|
||||
return td::Status::Error("depth mismatch");
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
Ref<Cell> Cell::virtualize(VirtualizationParameters virt) const {
|
||||
return VirtualCell::create(virt, Ref<Cell>(this));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Cell& c) {
|
||||
return os << c.get_hash().to_hex();
|
||||
}
|
||||
|
||||
} // namespace vm
|
89
crypto/vm/cells/Cell.h
Normal file
89
crypto/vm/cells/Cell.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/bitstring.h"
|
||||
|
||||
#include "vm/cells/CellHash.h"
|
||||
#include "vm/cells/CellTraits.h"
|
||||
#include "vm/cells/CellUsageTree.h"
|
||||
#include "vm/cells/LevelMask.h"
|
||||
#include "vm/cells/VirtualizationParameters.h"
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace vm {
|
||||
using td::Ref;
|
||||
class DataCell;
|
||||
|
||||
class Cell : public CellTraits {
|
||||
public:
|
||||
using LevelMask = detail::LevelMask;
|
||||
using VirtualizationParameters = detail::VirtualizationParameters;
|
||||
struct LoadedCell {
|
||||
Ref<DataCell> data_cell;
|
||||
VirtualizationParameters virt;
|
||||
CellUsageTree::NodePtr tree_node; // TODO: inline_vector?
|
||||
};
|
||||
|
||||
using Hash = CellHash;
|
||||
static_assert(std::is_standard_layout<Hash>::value, "Cell::Hash is not a standard layout type");
|
||||
static_assert(sizeof(Hash) == hash_bytes, "Cell::Hash size is not equal to hash_bytes");
|
||||
//typedef td::BitArray<hash_bits> hash_t;
|
||||
|
||||
Cell* make_copy() const final {
|
||||
throw WriteError();
|
||||
}
|
||||
|
||||
// load interface
|
||||
virtual td::Result<LoadedCell> load_cell() const = 0;
|
||||
virtual Ref<Cell> virtualize(VirtualizationParameters virt) const;
|
||||
virtual td::uint32 get_virtualization() const = 0;
|
||||
virtual CellUsageTree::NodePtr get_tree_node() const = 0;
|
||||
virtual bool is_loaded() const = 0;
|
||||
|
||||
// hash and level
|
||||
virtual LevelMask get_level_mask() const = 0;
|
||||
|
||||
// level helper function
|
||||
td::uint32 get_level() const {
|
||||
return get_level_mask().get_level();
|
||||
}
|
||||
|
||||
// hash helper functions
|
||||
const Hash get_hash(int level = max_level) const {
|
||||
return do_get_hash(level);
|
||||
}
|
||||
|
||||
// depth helper function
|
||||
td::uint16 get_depth(int level = max_level) const {
|
||||
return do_get_depth(level);
|
||||
}
|
||||
|
||||
td::Status check_equals_unloaded(const Ref<Cell>& other) const;
|
||||
|
||||
private:
|
||||
virtual td::uint16 do_get_depth(td::uint32 level) const = 0;
|
||||
virtual const Hash do_get_hash(td::uint32 level) const = 0;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Cell& c);
|
||||
} // namespace vm
|
593
crypto/vm/cells/CellBuilder.cpp
Normal file
593
crypto/vm/cells/CellBuilder.cpp
Normal file
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/CellBuilder.h"
|
||||
|
||||
#include "vm/cells/CellSlice.h"
|
||||
#include "vm/cells/DataCell.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/format.h"
|
||||
|
||||
#include "openssl/digest.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
using td::Ref;
|
||||
using td::RefAny;
|
||||
|
||||
/*
|
||||
*
|
||||
* CELL BUILDERS
|
||||
*
|
||||
*/
|
||||
|
||||
CellBuilder::~CellBuilder() {
|
||||
get_thread_safe_counter().add(-1);
|
||||
}
|
||||
|
||||
CellBuilder::CellBuilder() : bits(0), refs_cnt(0) {
|
||||
get_thread_safe_counter().add(+1);
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::finalize_copy(bool special) const {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
if (vm_state_interface) {
|
||||
vm_state_interface->register_cell_create();
|
||||
}
|
||||
auto res = DataCell::create(data, size(), td::span(refs.data(), size_refs()), special);
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << res.error();
|
||||
throw CellWriteError{};
|
||||
}
|
||||
CHECK(res.ok().not_null());
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::finalize_novm(bool special) {
|
||||
auto res = DataCell::create(data, size(), td::mutable_span(refs.data(), size_refs()), special);
|
||||
bits = refs_cnt = 0;
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << res.error();
|
||||
throw CellWriteError{};
|
||||
}
|
||||
CHECK(res.ok().not_null());
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::finalize(bool special) {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
if (vm_state_interface) {
|
||||
vm_state_interface->register_cell_create();
|
||||
}
|
||||
return finalize_novm(special);
|
||||
}
|
||||
|
||||
Ref<Cell> CellBuilder::create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level) {
|
||||
if (cell->is_loaded() && cell->get_level() <= virt_level && cell->get_virtualization() == 0) {
|
||||
CellSlice cs(NoVm{}, cell);
|
||||
if (cs.size_refs() == 0) {
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
return do_create_pruned_branch(std::move(cell), new_level, virt_level);
|
||||
}
|
||||
Ref<DataCell> CellBuilder::do_create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level) {
|
||||
auto level_mask = cell->get_level_mask().apply(virt_level);
|
||||
auto level = level_mask.get_level();
|
||||
if (new_level < level + 1) {
|
||||
throw CellWriteError();
|
||||
}
|
||||
CellBuilder cb;
|
||||
cb.store_long(static_cast<td::uint8>(Cell::SpecialType::PrunnedBranch), 8);
|
||||
cb.store_long(level_mask.apply_or(Cell::LevelMask::one_level(new_level)).get_mask(), 8);
|
||||
for (td::uint32 i = 0; i <= level; i++) {
|
||||
if (level_mask.is_significant(i)) {
|
||||
cb.store_bytes(cell->get_hash(i).as_slice());
|
||||
}
|
||||
}
|
||||
for (td::uint32 i = 0; i <= level; i++) {
|
||||
if (level_mask.is_significant(i)) {
|
||||
cb.store_long(cell->get_depth(i), 16);
|
||||
}
|
||||
}
|
||||
return cb.finalize(true);
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::create_merkle_proof(Ref<Cell> cell_proof) {
|
||||
CellBuilder cb;
|
||||
cb.store_long(static_cast<td::uint8>(Cell::SpecialType::MerkleProof), 8);
|
||||
cb.store_bytes(cell_proof->get_hash(0).as_slice());
|
||||
cb.store_long(cell_proof->get_depth(0), Cell::depth_bytes * 8);
|
||||
cb.store_ref(cell_proof);
|
||||
return cb.finalize(true);
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::create_merkle_update(Ref<Cell> from_proof, Ref<Cell> to_proof) {
|
||||
CellBuilder cb;
|
||||
cb.store_long(static_cast<td::uint8>(Cell::SpecialType::MerkleUpdate), 8);
|
||||
cb.store_bytes(from_proof->get_hash(0).as_slice());
|
||||
cb.store_bytes(to_proof->get_hash(0).as_slice());
|
||||
cb.store_long(from_proof->get_depth(0), Cell::depth_bytes * 8);
|
||||
cb.store_long(to_proof->get_depth(0), Cell::depth_bytes * 8);
|
||||
cb.store_ref(from_proof);
|
||||
cb.store_ref(to_proof);
|
||||
return cb.finalize(true);
|
||||
}
|
||||
|
||||
void CellBuilder::reset(void) {
|
||||
while (refs_cnt > 0) {
|
||||
refs[--refs_cnt].clear();
|
||||
}
|
||||
bits = 0;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::operator=(const CellBuilder& other) {
|
||||
bits = other.bits;
|
||||
refs_cnt = other.refs_cnt;
|
||||
refs = other.refs;
|
||||
std::memcpy(data, other.data, (bits + 7) >> 3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::operator=(CellBuilder&& other) {
|
||||
bits = other.bits;
|
||||
refs_cnt = other.refs_cnt;
|
||||
refs = std::move(other.refs);
|
||||
other.refs_cnt = 0;
|
||||
std::memcpy(data, other.data, (bits + 7) >> 3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CellBuilder::can_extend_by(std::size_t new_bits, unsigned new_refs) const {
|
||||
return (new_bits <= Cell::max_bits - bits && new_refs <= (unsigned)Cell::max_refs - refs_cnt);
|
||||
}
|
||||
|
||||
bool CellBuilder::can_extend_by(std::size_t new_bits) const {
|
||||
return new_bits <= Cell::max_bits - bits;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bytes(const unsigned char* str, std::size_t len) {
|
||||
ensure_throw(len <= Cell::max_bytes);
|
||||
return store_bits(str, len * 8);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bytes(const unsigned char* str, const unsigned char* end) {
|
||||
ensure_throw(end >= str && end <= str + Cell::max_bytes);
|
||||
return store_bits(str, (end - str) * 8);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bytes(const char* str, std::size_t len) {
|
||||
return store_bytes((const unsigned char*)(str), len);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bytes(const char* str, const char* end) {
|
||||
return store_bytes((const unsigned char*)(str), (const unsigned char*)(end));
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bytes(td::Slice s) {
|
||||
return store_bytes((const unsigned char*)(s.data()), (const unsigned char*)(s.data() + s.size()));
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bytes_bool(const unsigned char* str, std::size_t len) {
|
||||
return len <= Cell::max_bytes && store_bits_bool(str, len * 8);
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bytes_bool(const char* str, std::size_t len) {
|
||||
return len <= Cell::max_bytes && store_bits_bool((const unsigned char*)str, len * 8);
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bytes_bool(td::Slice s) {
|
||||
return store_bytes_bool((const unsigned char*)s.data(), s.size());
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bits_bool(const unsigned char* str, std::size_t bit_count, int bit_offset) {
|
||||
unsigned pos = bits;
|
||||
if (!prepare_reserve(bit_count)) {
|
||||
return false;
|
||||
}
|
||||
td::bitstring::bits_memcpy(data, pos, str, bit_offset, bit_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bits(const unsigned char* str, std::size_t bit_count, int bit_offset) {
|
||||
unsigned pos = bits;
|
||||
if (prepare_reserve(bit_count)) {
|
||||
td::bitstring::bits_memcpy(data, pos, str, bit_offset, bit_count);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bits(const td::BitSlice& bs) {
|
||||
return store_bits(bs.get_ptr(), bs.size(), bs.get_offs());
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bits(const char* str, std::size_t bit_count, int bit_offset) {
|
||||
return store_bits((const unsigned char*)str, bit_count, bit_offset);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bits(td::ConstBitPtr bs, std::size_t bit_count) {
|
||||
return store_bits(bs.ptr, bit_count, bs.offs);
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bits_bool(td::ConstBitPtr bs, std::size_t bit_count) {
|
||||
return store_bits_bool(bs.ptr, bit_count, bs.offs);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_bits_same(std::size_t bit_count, bool val) {
|
||||
unsigned pos = bits;
|
||||
if (prepare_reserve(bit_count)) {
|
||||
td::bitstring::bits_memset(data, pos, val, bit_count);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bits_same_bool(std::size_t bit_count, bool val) {
|
||||
unsigned pos = bits;
|
||||
if (!prepare_reserve(bit_count)) {
|
||||
return false;
|
||||
}
|
||||
td::bitstring::bits_memset(data, pos, val, bit_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool CellBuilder::prepare_reserve(std::size_t bit_count) {
|
||||
if (!can_extend_by(bit_count)) {
|
||||
return false;
|
||||
} else {
|
||||
bits += (unsigned)bit_count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
td::BitSliceWrite CellBuilder::reserve_slice(std::size_t bit_count) {
|
||||
unsigned offs = bits;
|
||||
if (prepare_reserve(bit_count)) {
|
||||
return td::BitSliceWrite{Ref<CellBuilder>{this}, data, offs, (unsigned)bit_count};
|
||||
} else {
|
||||
return td::BitSliceWrite{};
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::reserve_slice(std::size_t bit_count, td::BitSliceWrite& bsw) {
|
||||
unsigned offs = bits;
|
||||
if (prepare_reserve(bit_count)) {
|
||||
bsw.assign(Ref<CellBuilder>{this}, data, offs, (unsigned)bit_count);
|
||||
} else {
|
||||
bsw.forget();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CellBuilder::store_bool_bool(bool val) {
|
||||
if (can_extend_by_fast(1)) {
|
||||
store_long(val, 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CellBuilder::store_long_bool(long long val, unsigned val_bits) {
|
||||
if (val_bits > 64 || !can_extend_by(val_bits)) {
|
||||
return false;
|
||||
}
|
||||
store_long(val, val_bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CellBuilder::store_long_rchk_bool(long long val, unsigned val_bits) {
|
||||
if (val_bits > 64 || !can_extend_by(val_bits)) {
|
||||
return false;
|
||||
}
|
||||
if (val_bits < 64 && (val < static_cast<long long>(std::numeric_limits<td::uint64>::max() << (val_bits - 1)) ||
|
||||
val >= (1LL << (val_bits - 1)))) {
|
||||
return false;
|
||||
}
|
||||
store_long(val, val_bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CellBuilder::store_ulong_rchk_bool(unsigned long long val, unsigned val_bits) {
|
||||
if (val_bits > 64 || !can_extend_by(val_bits)) {
|
||||
return false;
|
||||
}
|
||||
if (val_bits < 64 && val >= (1ULL << val_bits)) {
|
||||
return false;
|
||||
}
|
||||
store_long(val, val_bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_long(long long val, unsigned val_bits) {
|
||||
return store_long_top(val << (64 - val_bits), val_bits);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_long_top(unsigned long long val, unsigned top_bits) {
|
||||
unsigned pos = bits;
|
||||
auto reserve_ok = prepare_reserve(top_bits);
|
||||
ensure_throw(reserve_ok);
|
||||
td::bitstring::bits_store_long_top(data, pos, val, top_bits);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CellBuilder::store_uint_less(unsigned upper_bound, unsigned long long val) {
|
||||
return val < upper_bound && store_long_bool(val, 32 - td::count_leading_zeroes32(upper_bound - 1));
|
||||
}
|
||||
|
||||
bool CellBuilder::store_uint_leq(unsigned upper_bound, unsigned long long val) {
|
||||
return val <= upper_bound && store_long_bool(val, 32 - td::count_leading_zeroes32(upper_bound));
|
||||
}
|
||||
|
||||
bool CellBuilder::store_int256_bool(const td::BigInt256& val, unsigned val_bits, bool sgnd) {
|
||||
unsigned pos = bits;
|
||||
if (!prepare_reserve(val_bits)) {
|
||||
return false;
|
||||
}
|
||||
if (val.export_bits(data, pos, val_bits, sgnd)) {
|
||||
return true;
|
||||
} else {
|
||||
bits = pos;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_int256(const td::BigInt256& val, unsigned val_bits, bool sgnd) {
|
||||
return ensure_pass(store_int256_bool(val, val_bits, sgnd));
|
||||
}
|
||||
|
||||
bool CellBuilder::store_int256_bool(td::RefInt256 val, unsigned val_bits, bool sgnd) {
|
||||
return val.not_null() && store_int256_bool(*val, val_bits, sgnd);
|
||||
}
|
||||
|
||||
bool CellBuilder::store_builder_ref_bool(vm::CellBuilder&& cb) {
|
||||
return store_ref_bool(cb.finalize());
|
||||
}
|
||||
|
||||
bool CellBuilder::store_ref_bool(Ref<Cell> ref) {
|
||||
if (refs_cnt < Cell::max_refs && ref.not_null()) {
|
||||
refs[refs_cnt++] = std::move(ref);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_ref(Ref<Cell> ref) {
|
||||
return ensure_pass(store_ref_bool(std::move(ref)));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_data_cell_bool(const DataCell& cell) {
|
||||
unsigned len = cell.size();
|
||||
if (can_extend_by(len, cell.size_refs())) {
|
||||
unsigned pos = bits;
|
||||
ensure_throw(prepare_reserve(len));
|
||||
td::bitstring::bits_memcpy(data, pos, cell.get_data(), 0, len);
|
||||
for (unsigned i = 0; i < cell.size_refs(); i++) {
|
||||
refs[refs_cnt++] = cell.get_ref(i);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_data_cell(const DataCell& cell) {
|
||||
return ensure_pass(append_data_cell_bool(cell));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_data_cell_bool(Ref<DataCell> cell_ref) {
|
||||
return append_data_cell_bool(*cell_ref);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_data_cell(Ref<DataCell> cell_ref) {
|
||||
return ensure_pass(append_data_cell_bool(std::move(cell_ref)));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_builder_bool(const CellBuilder& cb) {
|
||||
unsigned len = cb.size();
|
||||
if (can_extend_by(len, cb.size_refs())) {
|
||||
unsigned pos = bits;
|
||||
ensure_throw(prepare_reserve(len));
|
||||
td::bitstring::bits_memcpy(data, pos, cb.get_data(), 0, len);
|
||||
for (unsigned i = 0; i < cb.size_refs(); i++) {
|
||||
refs[refs_cnt++] = cb.get_ref(i);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_builder(const CellBuilder& cb) {
|
||||
return ensure_pass(append_builder_bool(cb));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_builder_bool(Ref<CellBuilder> cb_ref) {
|
||||
return append_builder_bool(*cb_ref);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_builder(Ref<CellBuilder> cb_ref) {
|
||||
return ensure_pass(append_builder_bool(std::move(cb_ref)));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_cellslice_bool(const CellSlice& cs) {
|
||||
unsigned len = cs.size();
|
||||
if (can_extend_by(len, cs.size_refs())) {
|
||||
int pos = bits;
|
||||
ensure_throw(prepare_reserve(len));
|
||||
td::bitstring::bits_memcpy(td::BitPtr{data, pos}, cs.data_bits(), len);
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
refs[refs_cnt++] = cs.prefetch_ref(i);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_cellslice(const CellSlice& cs) {
|
||||
return ensure_pass(append_cellslice_bool(cs));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_cellslice_bool(Ref<CellSlice> cs_ref) {
|
||||
return cs_ref.not_null() && append_cellslice_bool(*cs_ref);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::append_cellslice(Ref<CellSlice> cs) {
|
||||
return ensure_pass(append_cellslice_bool(cs));
|
||||
}
|
||||
|
||||
bool CellBuilder::append_cellslice_chk(const CellSlice& cs, unsigned size_ext) {
|
||||
return cs.size_ext() == size_ext && append_cellslice_bool(cs);
|
||||
}
|
||||
|
||||
bool CellBuilder::append_cellslice_chk(Ref<CellSlice> cs_ref, unsigned size_ext) {
|
||||
return cs_ref.not_null() && append_cellslice_chk(*cs_ref, size_ext);
|
||||
}
|
||||
|
||||
bool CellBuilder::append_bitstring(const td::BitString& bs) {
|
||||
return store_bits_bool(bs.cbits(), bs.size());
|
||||
}
|
||||
|
||||
bool CellBuilder::append_bitstring(Ref<td::BitString> bs_ref) {
|
||||
return bs_ref.not_null() && append_bitstring(*bs_ref);
|
||||
}
|
||||
|
||||
bool CellBuilder::append_bitstring_chk(const td::BitString& bs, unsigned size) {
|
||||
return bs.size() == size && store_bits_bool(bs.cbits(), size);
|
||||
}
|
||||
|
||||
bool CellBuilder::append_bitstring_chk(Ref<td::BitString> bs_ref, unsigned size) {
|
||||
return bs_ref.not_null() && append_bitstring_chk(*bs_ref, size);
|
||||
}
|
||||
|
||||
bool CellBuilder::append_bitslice(const td::BitSlice& bs) {
|
||||
return store_bits_bool(bs.bits(), bs.size());
|
||||
}
|
||||
|
||||
bool CellBuilder::store_maybe_ref(Ref<Cell> cell) {
|
||||
if (cell.is_null()) {
|
||||
return store_long_bool(0, 1);
|
||||
} else {
|
||||
return store_long_bool(1, 1) && store_ref_bool(std::move(cell));
|
||||
}
|
||||
}
|
||||
|
||||
void CellBuilder::flush(unsigned char d[2]) const {
|
||||
assert(refs_cnt <= Cell::max_refs && bits <= Cell::max_bits);
|
||||
|
||||
unsigned l = (bits >> 3);
|
||||
if (bits & 7) {
|
||||
int m = (0x80 >> (bits & 7));
|
||||
data[l] = static_cast<unsigned char>((data[l] & -m) | m);
|
||||
d[1] = static_cast<unsigned char>(2 * l + 1);
|
||||
} else {
|
||||
d[1] = static_cast<unsigned char>(2 * l);
|
||||
}
|
||||
d[0] = static_cast<unsigned char>(refs_cnt);
|
||||
}
|
||||
|
||||
const unsigned char* CellBuilder::compute_hash(unsigned char buffer[Cell::hash_bytes]) const {
|
||||
unsigned char tmp[2];
|
||||
flush(tmp);
|
||||
digest::SHA256 hasher(tmp, 2);
|
||||
hasher.feed(data, (bits + 7) >> 3);
|
||||
for (unsigned i = 0; i < refs_cnt; i++) {
|
||||
hasher.feed(refs[i]->get_hash().as_slice().data(), Cell::hash_bytes);
|
||||
}
|
||||
auto extracted_size = hasher.extract(buffer);
|
||||
DCHECK(extracted_size == Cell::hash_bytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int CellBuilder::serialize(unsigned char* buff, int buff_size) const {
|
||||
int len = get_serialized_size();
|
||||
if (len > buff_size) {
|
||||
return 0;
|
||||
}
|
||||
flush(buff);
|
||||
std::memcpy(buff + 2, data, len - 2);
|
||||
return len;
|
||||
}
|
||||
|
||||
CellBuilder* CellBuilder::make_copy() const {
|
||||
CellBuilder* c = new CellBuilder();
|
||||
if (!c) {
|
||||
throw CellWriteError();
|
||||
}
|
||||
c->bits = bits;
|
||||
std::memcpy(c->data, data, (bits + 7) >> 3);
|
||||
c->refs_cnt = refs_cnt;
|
||||
for (unsigned i = 0; i < refs_cnt; i++) {
|
||||
c->refs[i] = refs[i];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
CellSlice CellBuilder::as_cellslice() const& {
|
||||
return CellSlice{finalize_copy()};
|
||||
}
|
||||
|
||||
Ref<CellSlice> CellBuilder::as_cellslice_ref() const& {
|
||||
return Ref<CellSlice>{true, finalize_copy()};
|
||||
}
|
||||
|
||||
CellSlice CellBuilder::as_cellslice() && {
|
||||
return CellSlice{finalize()};
|
||||
}
|
||||
|
||||
Ref<CellSlice> CellBuilder::as_cellslice_ref() && {
|
||||
return Ref<CellSlice>{true, finalize()};
|
||||
}
|
||||
|
||||
bool CellBuilder::contents_equal(const CellSlice& cs) const {
|
||||
if (size() != cs.size() || size_refs() != cs.size_refs()) {
|
||||
return false;
|
||||
}
|
||||
if (td::bitstring::bits_memcmp(data_bits(), cs.data_bits(), size())) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < size_refs(); i++) {
|
||||
if (refs[i]->get_hash() != cs.prefetch_ref(i)->get_hash()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CellBuilder::serialize() const {
|
||||
unsigned char buff[Cell::max_serialized_bytes];
|
||||
int len = serialize(buff, sizeof(buff));
|
||||
return std::string(buff, buff + len);
|
||||
}
|
||||
|
||||
std::string CellBuilder::to_hex() const {
|
||||
unsigned char buff[Cell::max_serialized_bytes];
|
||||
int len = serialize(buff, sizeof(buff));
|
||||
char hex_buff[Cell::max_serialized_bytes * 2 + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
sprintf(hex_buff + 2 * i, "%02x", buff[i]);
|
||||
}
|
||||
return hex_buff;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const CellBuilder& cb) {
|
||||
return os << cb.to_hex();
|
||||
}
|
||||
} // namespace vm
|
238
crypto/vm/cells/CellBuilder.h
Normal file
238
crypto/vm/cells/CellBuilder.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/DataCell.h"
|
||||
#include "vm/cells/VirtualCell.h"
|
||||
#include "vm/vmstate.h"
|
||||
#include "common/refint.h"
|
||||
|
||||
#include "td/utils/ThreadSafeCounter.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
class CellSlice;
|
||||
class DataCell;
|
||||
|
||||
class CellBuilder : public td::CntObject {
|
||||
public:
|
||||
struct CellWriteError {};
|
||||
struct CellCreateError {};
|
||||
|
||||
private:
|
||||
unsigned bits;
|
||||
unsigned refs_cnt;
|
||||
std::array<Ref<Cell>, Cell::max_refs> refs;
|
||||
mutable unsigned char data[Cell::max_bytes];
|
||||
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() {
|
||||
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("CellBuilder");
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
CellBuilder();
|
||||
virtual ~CellBuilder() override;
|
||||
|
||||
static Ref<Cell> create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level = Cell::max_level);
|
||||
static Ref<DataCell> do_create_pruned_branch(Ref<Cell> cell, td::uint32 new_level,
|
||||
td::uint32 virt_level = Cell::max_level);
|
||||
static Ref<DataCell> create_merkle_proof(Ref<Cell> cell_proof);
|
||||
static Ref<DataCell> create_merkle_update(Ref<Cell> from_proof, Ref<Cell> to_proof);
|
||||
|
||||
unsigned get_refs_cnt() const {
|
||||
return refs_cnt;
|
||||
}
|
||||
unsigned get_bits() const {
|
||||
return bits;
|
||||
}
|
||||
unsigned size_refs() const {
|
||||
return refs_cnt;
|
||||
}
|
||||
unsigned size() const {
|
||||
return bits;
|
||||
}
|
||||
unsigned size_ext() const {
|
||||
return (refs_cnt << 16) + bits;
|
||||
}
|
||||
unsigned remaining_bits() const {
|
||||
return Cell::max_bits - bits;
|
||||
}
|
||||
unsigned remaining_refs() const {
|
||||
return Cell::max_refs - refs_cnt;
|
||||
}
|
||||
const unsigned char* get_data() const {
|
||||
return data;
|
||||
}
|
||||
td::ConstBitPtr data_bits() const {
|
||||
return data;
|
||||
}
|
||||
Ref<Cell> get_ref(unsigned idx) const {
|
||||
return idx < refs_cnt ? refs[idx] : Ref<Cell>{};
|
||||
}
|
||||
void reset();
|
||||
CellBuilder& operator=(const CellBuilder&);
|
||||
CellBuilder& operator=(CellBuilder&&);
|
||||
CellBuilder& store_bytes(const char* str, std::size_t len);
|
||||
CellBuilder& store_bytes(const char* str, const char* end);
|
||||
CellBuilder& store_bytes(const unsigned char* str, std::size_t len);
|
||||
CellBuilder& store_bytes(const unsigned char* str, const unsigned char* end);
|
||||
CellBuilder& store_bytes(td::Slice s);
|
||||
bool store_bytes_bool(const unsigned char* str, std::size_t len);
|
||||
bool store_bytes_bool(const char* str, std::size_t len);
|
||||
bool store_bytes_bool(td::Slice s);
|
||||
CellBuilder& store_bits(const unsigned char* str, std::size_t bit_count, int bit_offset = 0);
|
||||
CellBuilder& store_bits(const char* str, std::size_t bit_count, int bit_offset = 0);
|
||||
CellBuilder& store_bits(td::ConstBitPtr bs, std::size_t bit_count);
|
||||
CellBuilder& store_bits(const td::BitSlice& bs);
|
||||
CellBuilder& store_bits_same(std::size_t bit_count, bool val);
|
||||
bool store_bits_bool(const unsigned char* str, std::size_t bit_count, int bit_offset = 0);
|
||||
bool store_bits_bool(td::ConstBitPtr bs, std::size_t bit_count);
|
||||
template <unsigned n>
|
||||
bool store_bits_bool(const td::BitArray<n>& ba) {
|
||||
return store_bits_bool(ba.cbits(), n);
|
||||
}
|
||||
bool store_bits_same_bool(std::size_t bit_count, bool val);
|
||||
CellBuilder& store_zeroes(std::size_t bit_count) {
|
||||
return store_bits_same(bit_count, false);
|
||||
}
|
||||
CellBuilder& store_ones(std::size_t bit_count) {
|
||||
return store_bits_same(bit_count, true);
|
||||
}
|
||||
bool store_zeroes_bool(std::size_t bit_count) {
|
||||
return store_bits_same_bool(bit_count, false);
|
||||
}
|
||||
bool store_ones_bool(std::size_t bit_count) {
|
||||
return store_bits_same_bool(bit_count, true);
|
||||
}
|
||||
td::BitSliceWrite reserve_slice(std::size_t bit_count);
|
||||
CellBuilder& reserve_slice(std::size_t bit_count, td::BitSliceWrite& bsw);
|
||||
bool store_long_bool(long long val, unsigned val_bits = 64);
|
||||
bool store_long_rchk_bool(long long val, unsigned val_bits = 64);
|
||||
bool store_ulong_rchk_bool(unsigned long long val, unsigned val_bits = 64);
|
||||
bool store_uint_less(unsigned upper_bound, unsigned long long val);
|
||||
bool store_uint_leq(unsigned upper_bound, unsigned long long val);
|
||||
CellBuilder& store_long(long long val, unsigned val_bits = 64);
|
||||
// bool store_long_top_bool(unsigned long long val, unsigned top_bits);
|
||||
CellBuilder& store_long_top(unsigned long long val, unsigned top_bits);
|
||||
bool store_bool_bool(bool val);
|
||||
bool store_int256_bool(const td::BigInt256& val, unsigned val_bits, bool sgnd = true);
|
||||
bool store_int256_bool(td::RefInt256 val, unsigned val_bits, bool sgnd = true);
|
||||
bool store_uint256_bool(const td::BigInt256& val, unsigned val_bits) {
|
||||
return store_int256_bool(val, val_bits, false);
|
||||
}
|
||||
bool store_uint256_bool(td::RefInt256 val, unsigned val_bits) {
|
||||
return store_int256_bool(std::move(val), val_bits, false);
|
||||
}
|
||||
CellBuilder& store_int256(const td::BigInt256& val, unsigned val_bits, bool sgnd = true);
|
||||
CellBuilder& store_uint256(const td::BigInt256& val, unsigned val_bits) {
|
||||
return store_int256(val, val_bits, false);
|
||||
}
|
||||
bool store_builder_ref_bool(vm::CellBuilder&& cb);
|
||||
bool store_ref_bool(Ref<Cell> r);
|
||||
CellBuilder& store_ref(Ref<Cell> r);
|
||||
bool append_data_cell_bool(const DataCell& cell);
|
||||
CellBuilder& append_data_cell(const DataCell& cell);
|
||||
bool append_data_cell_bool(Ref<DataCell> cell_ref);
|
||||
CellBuilder& append_data_cell(Ref<DataCell> cell_ref);
|
||||
bool append_builder_bool(const CellBuilder& cb);
|
||||
CellBuilder& append_builder(const CellBuilder& cb);
|
||||
bool append_builder_bool(Ref<CellBuilder> cb_ref);
|
||||
CellBuilder& append_builder(Ref<CellBuilder> cb_ref);
|
||||
bool append_cellslice_bool(const CellSlice& cs);
|
||||
CellBuilder& append_cellslice(const CellSlice& cs);
|
||||
bool append_cellslice_bool(Ref<CellSlice> cs_ref);
|
||||
CellBuilder& append_cellslice(Ref<CellSlice> cs_ref);
|
||||
bool append_cellslice_chk(const CellSlice& cs, unsigned size_ext);
|
||||
bool append_cellslice_chk(Ref<CellSlice> cs, unsigned size_ext);
|
||||
bool append_bitstring(const td::BitString& bs);
|
||||
bool append_bitstring(Ref<td::BitString> bs_ref);
|
||||
bool append_bitstring_chk(const td::BitString& bs, unsigned size);
|
||||
bool append_bitstring_chk(Ref<td::BitString> bs, unsigned size);
|
||||
bool append_bitslice(const td::BitSlice& bs);
|
||||
bool store_maybe_ref(Ref<Cell> cell);
|
||||
bool contents_equal(const CellSlice& cs) const;
|
||||
CellBuilder* make_copy() const override;
|
||||
bool can_extend_by(std::size_t bits) const;
|
||||
bool can_extend_by(std::size_t bits, unsigned refs) const;
|
||||
Ref<DataCell> finalize_copy(bool special = false) const;
|
||||
Ref<DataCell> finalize(bool special = false);
|
||||
Ref<DataCell> finalize_novm(bool special = false);
|
||||
bool finalize_to(Ref<Cell>& res, bool special = false) {
|
||||
return (res = finalize(special)).not_null();
|
||||
}
|
||||
CellSlice as_cellslice() const &;
|
||||
CellSlice as_cellslice() &&;
|
||||
Ref<CellSlice> as_cellslice_ref() const &;
|
||||
Ref<CellSlice> as_cellslice_ref() &&;
|
||||
static td::int64 get_total_cell_builders() {
|
||||
return get_thread_safe_counter().sum();
|
||||
}
|
||||
int get_serialized_size() const {
|
||||
return ((bits + 23) >> 3);
|
||||
}
|
||||
int serialize(unsigned char* buff, int buff_size) const;
|
||||
std::string serialize() const;
|
||||
std::string to_hex() const;
|
||||
const unsigned char* compute_hash(unsigned char buffer[Cell::hash_bytes]) const;
|
||||
|
||||
const CellBuilder& ensure_pass(bool cond) const {
|
||||
ensure_throw(cond);
|
||||
return *this;
|
||||
}
|
||||
CellBuilder& ensure_pass(bool cond) {
|
||||
ensure_throw(cond);
|
||||
return *this;
|
||||
}
|
||||
void ensure_throw(bool cond) const {
|
||||
if (!cond) {
|
||||
throw CellCreateError{};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void flush(unsigned char d[2]) const;
|
||||
bool prepare_reserve(std::size_t bit_count);
|
||||
bool can_extend_by_fast(unsigned bits_req) const {
|
||||
return (int)bits <= (int)(Cell::max_bits - bits_req);
|
||||
}
|
||||
bool can_extend_by_fast(unsigned bits_req, unsigned refs_req) const {
|
||||
return (int)bits <= (int)(Cell::max_bits - bits_req) && (int)refs_cnt <= (int)(Cell::max_refs - refs_req);
|
||||
}
|
||||
bool can_extend_by_fast2(unsigned bits_req) const {
|
||||
return bits + bits_req <= Cell::max_bits;
|
||||
}
|
||||
bool can_extend_by_fast2(unsigned bits_req, unsigned refs_req) const {
|
||||
return bits + bits_req <= Cell::max_bits && refs_cnt + refs_req <= Cell::max_refs;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const CellBuilder& cb);
|
||||
|
||||
template <class T>
|
||||
CellBuilder& operator<<(CellBuilder& cb, const T& val) {
|
||||
return cb.ensure_pass(val.serialize(cb));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Ref<CellBuilder>& operator<<(Ref<CellBuilder>& cb_ref, const T& val) {
|
||||
bool res = val.serialize(cb_ref.write());
|
||||
cb_ref->ensure_throw(res);
|
||||
return cb_ref;
|
||||
}
|
||||
|
||||
} // namespace vm
|
28
crypto/vm/cells/CellHash.cpp
Normal file
28
crypto/vm/cells/CellHash.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/CellHash.h"
|
||||
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace vm {
|
||||
td::StringBuilder &operator<<(td::StringBuilder &sb, const CellHash &hash) {
|
||||
return sb << hash.to_hex();
|
||||
}
|
||||
} // namespace vm
|
92
crypto/vm/cells/CellHash.h
Normal file
92
crypto/vm/cells/CellHash.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/CellTraits.h"
|
||||
#include "common/bitstring.h"
|
||||
|
||||
#include "td/utils/as.h"
|
||||
|
||||
#include <array>
|
||||
namespace td {
|
||||
class StringBuilder;
|
||||
}
|
||||
|
||||
namespace vm {
|
||||
struct CellHash {
|
||||
public:
|
||||
td::Slice as_slice() const {
|
||||
return td::Slice(hash_.data(), hash_.size());
|
||||
}
|
||||
td::MutableSlice as_slice() {
|
||||
return td::MutableSlice(hash_.data(), hash_.size());
|
||||
}
|
||||
bool operator==(const CellHash& other) const {
|
||||
return hash_ == other.hash_;
|
||||
}
|
||||
bool operator<(const CellHash& other) const {
|
||||
return hash_ < other.hash_;
|
||||
}
|
||||
bool operator!=(const CellHash& other) const {
|
||||
return hash_ != other.hash_;
|
||||
}
|
||||
std::string to_hex() const {
|
||||
return td::ConstBitPtr{hash_.data()}.to_hex(hash_.size() * 8);
|
||||
}
|
||||
friend td::StringBuilder& operator<<(td::StringBuilder& sb, const CellHash& hash);
|
||||
td::ConstBitPtr bits() const {
|
||||
return td::ConstBitPtr{hash_.data()};
|
||||
}
|
||||
td::BitPtr bits() {
|
||||
return td::BitPtr{hash_.data()};
|
||||
}
|
||||
td::BitSlice as_bitslice() const {
|
||||
return td::BitSlice{hash_.data(), (unsigned int)hash_.size() * 8};
|
||||
}
|
||||
const std::array<td::uint8, CellTraits::hash_bytes>& as_array() const {
|
||||
return hash_;
|
||||
}
|
||||
|
||||
static CellHash from_slice(td::Slice slice) {
|
||||
CellHash res;
|
||||
CHECK(slice.size() == res.hash_.size());
|
||||
td::MutableSlice(res.hash_.data(), res.hash_.size()).copy_from(slice);
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<td::uint8, CellTraits::hash_bytes> hash_;
|
||||
};
|
||||
} // namespace vm
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<vm::CellHash> {
|
||||
typedef vm::CellHash argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const& s) const noexcept {
|
||||
return td::as<size_t>(s.as_slice().ubegin());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
namespace vm {
|
||||
template <class H>
|
||||
H AbslHashValue(H h, const CellHash& cell_hash) {
|
||||
return H::combine(std::move(h), std::hash<vm::CellHash>()(cell_hash));
|
||||
}
|
||||
} // namespace vm
|
1066
crypto/vm/cells/CellSlice.cpp
Normal file
1066
crypto/vm/cells/CellSlice.cpp
Normal file
File diff suppressed because it is too large
Load diff
328
crypto/vm/cells/CellSlice.h
Normal file
328
crypto/vm/cells/CellSlice.h
Normal file
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "vm/cells.h"
|
||||
|
||||
namespace td {
|
||||
class StringBuilder;
|
||||
}
|
||||
namespace vm {
|
||||
|
||||
struct NoVm {};
|
||||
struct NoVmOrd {};
|
||||
struct NoVmSpec {};
|
||||
|
||||
class CellSlice : public td::CntObject {
|
||||
Cell::VirtualizationParameters virt;
|
||||
Ref<DataCell> cell;
|
||||
CellUsageTree::NodePtr tree_node;
|
||||
unsigned bits_st, refs_st;
|
||||
unsigned bits_en, refs_en;
|
||||
mutable const unsigned char* ptr{nullptr};
|
||||
mutable unsigned long long z;
|
||||
mutable unsigned zd;
|
||||
|
||||
public:
|
||||
static constexpr long long fetch_long_eof = (static_cast<unsigned long long>(-1LL) << 63);
|
||||
static constexpr unsigned long long fetch_ulong_eof = (unsigned long long)-1LL;
|
||||
struct CellReadError {};
|
||||
|
||||
CellSlice(NoVm, Ref<Cell> cell_ref);
|
||||
CellSlice(NoVmOrd, Ref<Cell> cell_ref);
|
||||
CellSlice(NoVmSpec, Ref<Cell> cell_ref);
|
||||
CellSlice(Ref<DataCell> dc_ref);
|
||||
CellSlice(VirtualCell::LoadedCell loaded_cell);
|
||||
/*
|
||||
CellSlice(Ref<DataCell> dc_ref, unsigned _bits_en, unsigned _refs_en, unsigned _bits_st = 0, unsigned _refs_st = 0);*/
|
||||
CellSlice(const CellSlice& cs, unsigned _bits_en, unsigned _refs_en);
|
||||
CellSlice(const CellSlice& cs, unsigned _bits_en, unsigned _refs_en, unsigned _bits_st, unsigned _refs_st);
|
||||
CellSlice(const CellSlice&);
|
||||
CellSlice& operator=(const CellSlice& other) = default;
|
||||
CellSlice();
|
||||
Cell::LoadedCell move_as_loaded_cell();
|
||||
td::CntObject* make_copy() const override {
|
||||
return new CellSlice{*this};
|
||||
}
|
||||
void clear();
|
||||
bool load(VirtualCell::LoadedCell loaded_cell);
|
||||
bool load(NoVm, Ref<Cell> cell_ref);
|
||||
bool load(NoVmOrd, Ref<Cell> cell_ref);
|
||||
bool load(NoVmSpec, Ref<Cell> cell_ref);
|
||||
bool load(Ref<DataCell> dc_ref);
|
||||
bool load(Ref<Cell> cell);
|
||||
bool load_ord(Ref<Cell> cell);
|
||||
unsigned size() const {
|
||||
return bits_en - bits_st;
|
||||
}
|
||||
bool is_special() const {
|
||||
return cell->is_special();
|
||||
}
|
||||
bool is_valid() const {
|
||||
return cell.not_null();
|
||||
}
|
||||
Cell::SpecialType special_type() const {
|
||||
return cell->special_type();
|
||||
}
|
||||
int child_merkle_depth(int merkle_depth) const {
|
||||
if (merkle_depth == Cell::VirtualizationParameters::max_level()) {
|
||||
return merkle_depth;
|
||||
}
|
||||
if (cell->special_type() == Cell::SpecialType::MerkleProof ||
|
||||
cell->special_type() == Cell::SpecialType::MerkleUpdate) {
|
||||
merkle_depth++;
|
||||
}
|
||||
return merkle_depth;
|
||||
}
|
||||
unsigned size_refs() const {
|
||||
return refs_en - refs_st;
|
||||
}
|
||||
unsigned size_ext() const {
|
||||
return size() + (size_refs() << 16);
|
||||
}
|
||||
bool have(unsigned bits) const {
|
||||
return bits <= size();
|
||||
}
|
||||
bool have(unsigned bits, unsigned refs) const {
|
||||
return bits <= size() && refs <= size_refs();
|
||||
}
|
||||
bool have_ext(unsigned ext_size) const {
|
||||
return have(ext_size & 0xffff, ext_size >> 16);
|
||||
}
|
||||
bool empty() const {
|
||||
return !size();
|
||||
}
|
||||
bool empty_ext() const {
|
||||
return !size() && !size_refs();
|
||||
}
|
||||
bool have_refs(unsigned refs = 1) const {
|
||||
return refs <= size_refs();
|
||||
}
|
||||
bool advance(unsigned bits);
|
||||
bool advance_refs(unsigned refs);
|
||||
bool advance_ext(unsigned bits_refs);
|
||||
bool advance_ext(unsigned bits, unsigned refs);
|
||||
unsigned cur_pos() const {
|
||||
return bits_st;
|
||||
}
|
||||
unsigned cur_ref() const {
|
||||
return refs_st;
|
||||
}
|
||||
const unsigned char* data() const {
|
||||
return cell->get_data();
|
||||
}
|
||||
td::ConstBitPtr data_bits() const {
|
||||
return td::ConstBitPtr{data(), (int)cur_pos()};
|
||||
}
|
||||
int subtract_base_ext(const CellSlice& base) {
|
||||
return (bits_st - base.bits_st) | ((refs_st - base.refs_st) << 16);
|
||||
}
|
||||
unsigned get_cell_level() const;
|
||||
unsigned get_level() const;
|
||||
int fetch_octet();
|
||||
int prefetch_octet() const;
|
||||
unsigned long long prefetch_ulong_top(unsigned& bits) const;
|
||||
unsigned long long fetch_ulong(unsigned bits);
|
||||
unsigned long long prefetch_ulong(unsigned bits) const;
|
||||
long long fetch_long(unsigned bits);
|
||||
long long prefetch_long(unsigned bits) const;
|
||||
bool fetch_long_bool(unsigned bits, long long& res);
|
||||
bool prefetch_long_bool(unsigned bits, long long& res) const;
|
||||
bool fetch_ulong_bool(unsigned bits, unsigned long long& res);
|
||||
bool prefetch_ulong_bool(unsigned bits, unsigned long long& res) const;
|
||||
bool fetch_bool_to(bool& res);
|
||||
bool fetch_bool_to(int& res);
|
||||
bool fetch_bool_to(int& res, int mask);
|
||||
bool fetch_uint_to(unsigned bits, unsigned long long& res);
|
||||
bool fetch_uint_to(unsigned bits, long long& res);
|
||||
bool fetch_uint_to(unsigned bits, unsigned long& res);
|
||||
bool fetch_uint_to(unsigned bits, long& res);
|
||||
bool fetch_uint_to(unsigned bits, unsigned& res);
|
||||
bool fetch_uint_to(unsigned bits, int& res);
|
||||
bool fetch_uint_less(unsigned upper_bound, int& res);
|
||||
bool fetch_uint_less(unsigned upper_bound, unsigned& res);
|
||||
bool fetch_uint_leq(unsigned upper_bound, int& res);
|
||||
bool fetch_uint_leq(unsigned upper_bound, unsigned& res);
|
||||
bool fetch_int_to(unsigned bits, long long& res);
|
||||
bool fetch_int_to(unsigned bits, int& res);
|
||||
int bselect(unsigned bits, unsigned long long mask) const;
|
||||
int bselect_ext(unsigned bits, unsigned long long mask) const;
|
||||
int bit_at(unsigned i) const {
|
||||
return have(i) ? data_bits()[i] : -1;
|
||||
}
|
||||
td::RefInt256 fetch_int256(unsigned bits, bool sgnd = true);
|
||||
td::RefInt256 prefetch_int256(unsigned bits, bool sgnd = true) const;
|
||||
td::RefInt256 prefetch_int256_zeroext(unsigned bits, bool sgnd = true) const;
|
||||
bool fetch_int256_to(unsigned bits, td::RefInt256& res, bool sgnd = true) {
|
||||
return (res = fetch_int256(bits, sgnd)).not_null();
|
||||
}
|
||||
bool fetch_uint256_to(unsigned bits, td::RefInt256& res) {
|
||||
return (res = fetch_int256(bits, false)).not_null();
|
||||
}
|
||||
Ref<Cell> prefetch_ref(unsigned offset = 0) const;
|
||||
Ref<Cell> fetch_ref();
|
||||
bool fetch_ref_to(Ref<Cell>& ref) {
|
||||
return (ref = fetch_ref()).not_null();
|
||||
}
|
||||
bool prefetch_ref_to(Ref<Cell>& ref, unsigned offset = 0) const {
|
||||
return (ref = prefetch_ref(offset)).not_null();
|
||||
}
|
||||
td::BitSlice fetch_bits(unsigned bits);
|
||||
td::BitSlice prefetch_bits(unsigned bits) const;
|
||||
td::Ref<CellSlice> fetch_subslice(unsigned bits, unsigned refs = 0);
|
||||
td::Ref<CellSlice> prefetch_subslice(unsigned bits, unsigned refs = 0) const;
|
||||
td::Ref<CellSlice> fetch_subslice_ext(unsigned size);
|
||||
td::Ref<CellSlice> prefetch_subslice_ext(unsigned size) const;
|
||||
td::Ref<td::BitString> fetch_bitstring(unsigned size);
|
||||
td::Ref<td::BitString> prefetch_bitstring(unsigned size) const;
|
||||
bool fetch_subslice_to(unsigned bits, td::Ref<CellSlice>& res) {
|
||||
return (res = fetch_subslice(bits)).not_null();
|
||||
}
|
||||
bool fetch_subslice_ext_to(unsigned bits, td::Ref<CellSlice>& res) {
|
||||
return (res = fetch_subslice_ext(bits)).not_null();
|
||||
}
|
||||
bool fetch_bitstring_to(unsigned bits, td::Ref<td::BitString>& res) {
|
||||
return (res = fetch_bitstring(bits)).not_null();
|
||||
}
|
||||
bool fetch_bits_to(td::BitPtr buffer, unsigned bits);
|
||||
bool prefetch_bits_to(td::BitPtr buffer, unsigned bits) const;
|
||||
template <unsigned n>
|
||||
bool fetch_bits_to(td::BitArray<n>& buffer) {
|
||||
return fetch_bits_to(buffer.bits(), n);
|
||||
}
|
||||
template <unsigned n>
|
||||
bool prefetch_bits_to(td::BitArray<n>& buffer) const {
|
||||
return prefetch_bits_to(buffer.bits(), n);
|
||||
}
|
||||
bool fetch_bytes(unsigned char* buffer, unsigned bytes);
|
||||
bool prefetch_bytes(unsigned char* buffer, unsigned bytes) const;
|
||||
td::BitSlice as_bitslice() const {
|
||||
return prefetch_bits(size());
|
||||
}
|
||||
bool begins_with(unsigned bits, unsigned long long value) const;
|
||||
bool begins_with(unsigned long long value) const;
|
||||
bool begins_with_skip(unsigned bits, unsigned long long value) {
|
||||
return begins_with(bits, value) && advance(bits);
|
||||
}
|
||||
bool begins_with_skip(unsigned long long value);
|
||||
bool only_first(unsigned bits, unsigned refs = 0);
|
||||
bool only_ext(unsigned size);
|
||||
bool skip_first(unsigned bits, unsigned refs = 0);
|
||||
bool skip_ext(unsigned size);
|
||||
bool only_last(unsigned bits, unsigned refs = 0);
|
||||
bool skip_last(unsigned bits, unsigned refs = 0);
|
||||
bool cut_tail(const CellSlice& tail_cs);
|
||||
int remove_trailing();
|
||||
int count_leading(bool bit) const;
|
||||
int count_trailing(bool bit) const;
|
||||
int lex_cmp(const CellSlice& cs2) const;
|
||||
int common_prefix_len(const CellSlice& cs2) const;
|
||||
bool is_prefix_of(const CellSlice& cs2) const;
|
||||
bool is_prefix_of(td::ConstBitPtr bs, unsigned len) const;
|
||||
bool is_suffix_of(const CellSlice& cs2) const;
|
||||
bool has_prefix(const CellSlice& cs2) const;
|
||||
bool has_prefix(td::ConstBitPtr bs, unsigned len) const;
|
||||
bool has_suffix(const CellSlice& cs2) const;
|
||||
bool is_proper_prefix_of(const CellSlice& cs2) const;
|
||||
bool is_proper_suffix_of(const CellSlice& cs2) const;
|
||||
// int common_prefix_len(const td::BitSlice& bs, unsigned offs = 0, unsigned max_len = 0xffffffffU) const;
|
||||
int common_prefix_len(td::ConstBitPtr bs, unsigned len) const;
|
||||
// bool is_prefix_of(const td::BitSlice& bs, unsigned offs = 0, unsigned max_len = 0xffffffffU) const;
|
||||
bool contents_equal(const CellSlice& cs2) const;
|
||||
void dump(std::ostream& os, int level = 0, bool endl = true) const;
|
||||
void dump_hex(std::ostream& os, int mode = 0, bool endl = false) const;
|
||||
void print_rec(std::ostream& os, int indent = 0) const;
|
||||
void error() const {
|
||||
throw CellReadError{};
|
||||
}
|
||||
bool chk(bool cond) const {
|
||||
if (!cond) {
|
||||
error();
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
bool have_chk(unsigned bits) const {
|
||||
return chk(have(bits));
|
||||
}
|
||||
bool have_chk(unsigned bits, unsigned refs) const {
|
||||
return chk(have(bits, refs));
|
||||
}
|
||||
bool have_refs_chk(unsigned refs = 1) const {
|
||||
return chk(have_refs(refs));
|
||||
}
|
||||
CellSlice operator+(unsigned offs) const {
|
||||
offs = std::min(offs, size());
|
||||
return CellSlice{*this, size() - offs, size_refs(), offs, 0};
|
||||
}
|
||||
|
||||
private:
|
||||
void init_bits_refs();
|
||||
void init_preload() const;
|
||||
void preload_at_least(unsigned req_bits) const;
|
||||
Cell::VirtualizationParameters child_virt() const {
|
||||
return Cell::VirtualizationParameters(static_cast<td::uint8>(child_merkle_depth(virt.get_level())),
|
||||
virt.get_virtualization());
|
||||
}
|
||||
};
|
||||
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, const CellSlice& cs);
|
||||
|
||||
bool cell_builder_add_slice_bool(CellBuilder& cb, const CellSlice& cs);
|
||||
CellBuilder& cell_builder_add_slice(CellBuilder& cb, const CellSlice& cs);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CellSlice cs);
|
||||
std::ostream& operator<<(std::ostream& os, Ref<CellSlice> cs_ref);
|
||||
|
||||
template <class T>
|
||||
CellSlice& operator>>(CellSlice& cs, T& val) {
|
||||
cs.chk(val.deserialize(cs));
|
||||
return cs;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Ref<CellSlice>& operator>>(Ref<CellSlice>& cs_ref, T& val) {
|
||||
bool res = val.deserialize(cs_ref.write());
|
||||
cs_ref->chk(res);
|
||||
return cs_ref;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
CellSlice& operator>>(CellSlice& cs, const T& val) {
|
||||
cs.chk(val.deserialize(cs));
|
||||
return cs;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Ref<CellSlice>& operator>>(Ref<CellSlice>& cs_ref, const T& val) {
|
||||
bool res = val.deserialize(cs_ref.write());
|
||||
cs_ref->chk(res);
|
||||
return cs_ref;
|
||||
}
|
||||
|
||||
// If can_be_special is not null, then it is allowed to load special cell
|
||||
// Flag whether loaded cell is actually special will be stored into can_be_special
|
||||
CellSlice load_cell_slice(const Ref<Cell>& cell);
|
||||
Ref<CellSlice> load_cell_slice_ref(const Ref<Cell>& cell);
|
||||
CellSlice load_cell_slice_special(const Ref<Cell>& cell, bool& is_special);
|
||||
Ref<CellSlice> load_cell_slice_ref_special(const Ref<Cell>& cell, bool& is_special);
|
||||
void print_load_cell(std::ostream& os, Ref<Cell> cell, int indent = 0);
|
||||
|
||||
} // namespace vm
|
45
crypto/vm/cells/CellTraits.cpp
Normal file
45
crypto/vm/cells/CellTraits.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/CellTraits.h"
|
||||
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace vm {
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, CellTraits::SpecialType special_type) {
|
||||
switch (special_type) {
|
||||
case CellTraits::SpecialType::Ordinary:
|
||||
sb << "Ordinary";
|
||||
break;
|
||||
case CellTraits::SpecialType::MerkleProof:
|
||||
sb << "MerkleProof";
|
||||
break;
|
||||
case CellTraits::SpecialType::MerkleUpdate:
|
||||
sb << "MerkleUpdate";
|
||||
break;
|
||||
case CellTraits::SpecialType::PrunnedBranch:
|
||||
sb << "PrunnedBranch";
|
||||
break;
|
||||
case CellTraits::SpecialType::Library:
|
||||
sb << "Library";
|
||||
break;
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
} // namespace vm
|
52
crypto/vm/cells/CellTraits.h
Normal file
52
crypto/vm/cells/CellTraits.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "td/utils/int_types.h"
|
||||
#include "common/refcnt.hpp"
|
||||
|
||||
namespace td {
|
||||
class StringBuilder;
|
||||
}
|
||||
|
||||
namespace vm {
|
||||
class CellTraits : public td::CntObject {
|
||||
public:
|
||||
enum class SpecialType : td::uint8 {
|
||||
Ordinary = 0,
|
||||
PrunnedBranch = 1,
|
||||
Library = 2,
|
||||
MerkleProof = 3,
|
||||
MerkleUpdate = 4
|
||||
};
|
||||
enum {
|
||||
max_refs = 4,
|
||||
max_bytes = 128,
|
||||
max_bits = 1023,
|
||||
hash_bytes = 32,
|
||||
hash_bits = hash_bytes * 8,
|
||||
depth_bytes = 2,
|
||||
depth_bits = depth_bytes * 8,
|
||||
max_level = 3,
|
||||
max_depth = 1024,
|
||||
max_virtualization = 7,
|
||||
max_serialized_bytes = 2 + max_bytes + (max_level + 1) * (hash_bytes + depth_bytes)
|
||||
};
|
||||
};
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, CellTraits::SpecialType special_type);
|
||||
} // namespace vm
|
136
crypto/vm/cells/CellUsageTree.cpp
Normal file
136
crypto/vm/cells/CellUsageTree.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/CellUsageTree.h"
|
||||
|
||||
namespace vm {
|
||||
//
|
||||
// CellUsageTree::NodePtr
|
||||
//
|
||||
bool CellUsageTree::NodePtr::on_load() const {
|
||||
auto tree = tree_weak_.lock();
|
||||
if (!tree) {
|
||||
return false;
|
||||
}
|
||||
tree->on_load(node_id_);
|
||||
return true;
|
||||
}
|
||||
|
||||
CellUsageTree::NodePtr CellUsageTree::NodePtr::create_child(unsigned ref_id) const {
|
||||
auto tree = tree_weak_.lock();
|
||||
if (!tree) {
|
||||
return {};
|
||||
}
|
||||
return {tree_weak_, tree->create_child(node_id_, ref_id)};
|
||||
}
|
||||
|
||||
bool CellUsageTree::NodePtr::is_from_tree(const CellUsageTree* master_tree) const {
|
||||
DCHECK(master_tree);
|
||||
auto tree = tree_weak_.lock();
|
||||
if (tree.get() != master_tree) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CellUsageTree::NodePtr::mark_path(CellUsageTree* master_tree) const {
|
||||
DCHECK(master_tree);
|
||||
auto tree = tree_weak_.lock();
|
||||
if (tree.get() != master_tree) {
|
||||
return false;
|
||||
}
|
||||
master_tree->mark_path(node_id_);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// CellUsageTree
|
||||
//
|
||||
CellUsageTree::NodePtr CellUsageTree::root_ptr() {
|
||||
return {shared_from_this(), 1};
|
||||
}
|
||||
|
||||
CellUsageTree::NodeId CellUsageTree::root_id() const {
|
||||
return 1;
|
||||
};
|
||||
|
||||
bool CellUsageTree::is_loaded(NodeId node_id) const {
|
||||
if (use_mark_) {
|
||||
return nodes_[node_id].has_mark;
|
||||
}
|
||||
return nodes_[node_id].is_loaded;
|
||||
}
|
||||
|
||||
bool CellUsageTree::has_mark(NodeId node_id) const {
|
||||
return nodes_[node_id].has_mark;
|
||||
}
|
||||
|
||||
void CellUsageTree::set_mark(NodeId node_id, bool mark) {
|
||||
if (node_id == 0) {
|
||||
return;
|
||||
}
|
||||
nodes_[node_id].has_mark = mark;
|
||||
}
|
||||
|
||||
void CellUsageTree::mark_path(NodeId node_id) {
|
||||
auto cur_node_id = get_parent(node_id);
|
||||
while (cur_node_id != 0) {
|
||||
if (has_mark(cur_node_id)) {
|
||||
break;
|
||||
}
|
||||
set_mark(cur_node_id);
|
||||
cur_node_id = get_parent(cur_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
CellUsageTree::NodeId CellUsageTree::get_parent(NodeId node_id) {
|
||||
return nodes_[node_id].parent;
|
||||
}
|
||||
|
||||
CellUsageTree::NodeId CellUsageTree::get_child(NodeId node_id, unsigned ref_id) {
|
||||
DCHECK(ref_id < CellTraits::max_refs);
|
||||
return nodes_[node_id].children[ref_id];
|
||||
}
|
||||
|
||||
void CellUsageTree::set_use_mark_for_is_loaded(bool use_mark) {
|
||||
use_mark_ = use_mark;
|
||||
}
|
||||
|
||||
void CellUsageTree::on_load(NodeId node_id) {
|
||||
nodes_[node_id].is_loaded = true;
|
||||
}
|
||||
|
||||
CellUsageTree::NodeId CellUsageTree::create_child(NodeId node_id, unsigned ref_id) {
|
||||
DCHECK(ref_id < CellTraits::max_refs);
|
||||
NodeId res = nodes_[node_id].children[ref_id];
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
res = create_node(node_id);
|
||||
nodes_[node_id].children[ref_id] = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
CellUsageTree::NodeId CellUsageTree::create_node(NodeId parent) {
|
||||
NodeId res = static_cast<NodeId>(nodes_.size());
|
||||
nodes_.emplace_back();
|
||||
nodes_.back().parent = parent;
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace vm
|
75
crypto/vm/cells/CellUsageTree.h
Normal file
75
crypto/vm/cells/CellUsageTree.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "vm/cells/CellTraits.h"
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
namespace vm {
|
||||
class CellUsageTree : public std::enable_shared_from_this<CellUsageTree> {
|
||||
public:
|
||||
using NodeId = td::uint32;
|
||||
|
||||
struct NodePtr {
|
||||
public:
|
||||
NodePtr() = default;
|
||||
NodePtr(std::weak_ptr<CellUsageTree> tree_weak, NodeId node_id)
|
||||
: tree_weak_(std::move(tree_weak)), node_id_(node_id) {
|
||||
}
|
||||
bool empty() const {
|
||||
return node_id_ == 0 || tree_weak_.expired();
|
||||
}
|
||||
|
||||
bool on_load() const;
|
||||
NodePtr create_child(unsigned ref_id) const;
|
||||
bool mark_path(CellUsageTree* master_tree) const;
|
||||
bool is_from_tree(const CellUsageTree* master_tree) const;
|
||||
|
||||
private:
|
||||
std::weak_ptr<CellUsageTree> tree_weak_;
|
||||
NodeId node_id_{0};
|
||||
};
|
||||
|
||||
NodePtr root_ptr();
|
||||
NodeId root_id() const;
|
||||
bool is_loaded(NodeId node_id) const;
|
||||
bool has_mark(NodeId node_id) const;
|
||||
void set_mark(NodeId node_id, bool mark = true);
|
||||
void mark_path(NodeId node_id);
|
||||
NodeId get_parent(NodeId node_id);
|
||||
NodeId get_child(NodeId node_id, unsigned ref_id);
|
||||
void set_use_mark_for_is_loaded(bool use_mark = true);
|
||||
NodeId create_child(NodeId node_id, unsigned ref_id);
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
bool is_loaded{false};
|
||||
bool has_mark{false};
|
||||
NodeId parent{0};
|
||||
std::array<td::uint32, CellTraits::max_refs> children{};
|
||||
};
|
||||
bool use_mark_{false};
|
||||
std::vector<Node> nodes_{2};
|
||||
|
||||
void on_load(NodeId node_id);
|
||||
NodeId create_node(NodeId parent);
|
||||
};
|
||||
} // namespace vm
|
92
crypto/vm/cells/CellWithStorage.h
Normal file
92
crypto/vm/cells/CellWithStorage.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
template <class CellT, size_t Size = 0>
|
||||
class CellWithArrayStorage : public CellT {
|
||||
public:
|
||||
template <class... ArgsT>
|
||||
CellWithArrayStorage(ArgsT&&... args) : CellT(std::forward<ArgsT>(args)...) {
|
||||
}
|
||||
~CellWithArrayStorage() {
|
||||
CellT::destroy_storage(get_storage());
|
||||
}
|
||||
template <class... ArgsT>
|
||||
static std::unique_ptr<CellT> create(size_t storage_size, ArgsT&&... args) {
|
||||
static_assert(CellT::max_storage_size <= 40 * 8, "");
|
||||
//size = 128 + 32 + 8;
|
||||
auto size = (storage_size + 7) / 8;
|
||||
#define CASE(size) \
|
||||
case (size): \
|
||||
return std::make_unique<CellWithArrayStorage<CellT, (size)*8>>(std::forward<ArgsT>(args)...);
|
||||
#define CASE2(offset) CASE(offset) CASE(offset + 1)
|
||||
#define CASE8(offset) CASE2(offset) CASE2(offset + 2) CASE2(offset + 4) CASE2(offset + 6)
|
||||
#define CASE32(offset) CASE8(offset) CASE8(offset + 8) CASE8(offset + 16) CASE8(offset + 24)
|
||||
switch (size) { CASE32(0) CASE8(32) }
|
||||
#undef CASE
|
||||
#undef CASE2
|
||||
#undef CASE8
|
||||
#undef CASE32
|
||||
LOG(FATAL) << "TOO BIG " << storage_size;
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
private:
|
||||
alignas(alignof(void*)) char storage_[Size];
|
||||
|
||||
const char* get_storage() const final {
|
||||
return storage_;
|
||||
}
|
||||
char* get_storage() final {
|
||||
return storage_;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CellT>
|
||||
class CellWithUniquePtrStorage : public CellT {
|
||||
public:
|
||||
template <class... ArgsT>
|
||||
CellWithUniquePtrStorage(size_t storage_size, ArgsT&&... args)
|
||||
: CellT(std::forward<ArgsT>(args)...), storage_(std::make_unique<char[]>(storage_size)) {
|
||||
}
|
||||
~CellWithUniquePtrStorage() {
|
||||
CellT::destroy_storage(get_storage());
|
||||
}
|
||||
|
||||
template <class... ArgsT>
|
||||
static std::unique_ptr<CellT> create(size_t storage_size, ArgsT&&... args) {
|
||||
return std::make_unique<CellWithUniquePtrStorage>(storage_size, std::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> storage_;
|
||||
|
||||
const char* get_storage() const final {
|
||||
CHECK(storage_);
|
||||
return storage_.get();
|
||||
}
|
||||
char* get_storage() final {
|
||||
CHECK(storage_);
|
||||
return storage_.get();
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace vm
|
368
crypto/vm/cells/DataCell.cpp
Normal file
368
crypto/vm/cells/DataCell.cpp
Normal file
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/DataCell.h"
|
||||
|
||||
#include "openssl/digest.h"
|
||||
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
#include "vm/cells/CellWithStorage.h"
|
||||
|
||||
namespace vm {
|
||||
std::unique_ptr<DataCell> DataCell::create_empty_data_cell(Info info) {
|
||||
return detail::CellWithUniquePtrStorage<DataCell>::create(info.get_storage_size(), info);
|
||||
}
|
||||
|
||||
DataCell::DataCell(Info info) : info_(std::move(info)) {
|
||||
get_thread_safe_counter().add(1);
|
||||
}
|
||||
DataCell::~DataCell() {
|
||||
get_thread_safe_counter().add(-1);
|
||||
}
|
||||
|
||||
void DataCell::destroy_storage(char* storage) {
|
||||
auto* refs = info_.get_refs(storage);
|
||||
for (size_t i = 0; i < get_refs_cnt(); i++) {
|
||||
Ref<Cell>(refs[i], Ref<Cell>::acquire_t{}); // call destructor
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<Ref<DataCell>> DataCell::create(td::ConstBitPtr data, unsigned bits, td::Span<Ref<Cell>> refs,
|
||||
bool special) {
|
||||
std::array<Ref<Cell>, max_refs> copied_refs;
|
||||
CHECK(refs.size() <= copied_refs.size());
|
||||
for (size_t i = 0; i < refs.size(); i++) {
|
||||
copied_refs[i] = refs[i];
|
||||
}
|
||||
return create(std::move(data), bits, td::MutableSpan<Ref<Cell>>(copied_refs.data(), refs.size()), special);
|
||||
}
|
||||
|
||||
DataCell::SpecialType DataCell::special_type() const {
|
||||
if (is_special()) {
|
||||
return static_cast<SpecialType>(td::bitstring::bits_load_ulong(get_data(), 8));
|
||||
}
|
||||
return SpecialType::Ordinary;
|
||||
}
|
||||
|
||||
td::Result<Ref<DataCell>> DataCell::create(td::ConstBitPtr data, unsigned bits, td::MutableSpan<Ref<Cell>> refs,
|
||||
bool special) {
|
||||
for (auto& ref : refs) {
|
||||
if (ref.is_null()) {
|
||||
return td::Status::Error("Has null cell reference");
|
||||
}
|
||||
}
|
||||
|
||||
SpecialType type = SpecialType::Ordinary;
|
||||
if (special) {
|
||||
if (bits < 8) {
|
||||
return td::Status::Error("Not enough data for a special cell");
|
||||
}
|
||||
type = static_cast<SpecialType>(td::bitstring::bits_load_ulong(data, 8));
|
||||
if (type == SpecialType::Ordinary) {
|
||||
return td::Status::Error("Special cell has Ordinary type");
|
||||
}
|
||||
}
|
||||
|
||||
LevelMask level_mask;
|
||||
td::uint32 virtualization = 0;
|
||||
switch (type) {
|
||||
case SpecialType::Ordinary: {
|
||||
for (auto& ref : refs) {
|
||||
level_mask = level_mask.apply_or(ref->get_level_mask());
|
||||
virtualization = td::max(virtualization, ref->get_virtualization());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SpecialType::PrunnedBranch: {
|
||||
if (refs.size() != 0) {
|
||||
return td::Status::Error("PrunnedBranch special cell has a cell reference");
|
||||
}
|
||||
if (bits < 16) {
|
||||
return td::Status::Error("Not enough data for a PrunnedBranch special cell");
|
||||
}
|
||||
level_mask = LevelMask((td::bitstring::bits_load_ulong(data + 8, 8)) & 0xff);
|
||||
auto level = level_mask.get_level();
|
||||
if (level > max_level || level == 0) {
|
||||
return td::Status::Error("Prunned Branch has an invalid level");
|
||||
}
|
||||
if (bits != (2 + level_mask.apply(level - 1).get_hashes_count() * (hash_bytes + depth_bytes)) * 8) {
|
||||
return td::Status::Error("Not enouch data for a PrunnedBranch special cell");
|
||||
}
|
||||
// depth will be checked later!
|
||||
break;
|
||||
}
|
||||
|
||||
case SpecialType::Library: {
|
||||
if (bits != 8 + hash_bytes * 8) {
|
||||
return td::Status::Error("Not enouch data for a Library special cell");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SpecialType::MerkleProof: {
|
||||
if (bits != 8 + (hash_bytes + depth_bytes) * 8) {
|
||||
return td::Status::Error("Not enouch data for a MerkleProof special cell");
|
||||
}
|
||||
if (refs.size() != 1) {
|
||||
return td::Status::Error("Wrong references count for a MerkleProof special cell");
|
||||
}
|
||||
if (td::bitstring::bits_memcmp(data + 8, refs[0]->get_hash(0).as_bitslice().get_ptr(), hash_bits) != 0) {
|
||||
return td::Status::Error("Hash mismatch in a MerkleProof special cell");
|
||||
}
|
||||
if (td::bitstring::bits_load_ulong(data + 8 + hash_bits, depth_bytes * 8) != refs[0]->get_depth(0)) {
|
||||
return td::Status::Error("Depth mismatch in a MerkleProof special cell");
|
||||
}
|
||||
level_mask = refs[0]->get_level_mask().shift_right();
|
||||
virtualization = refs[0]->get_virtualization();
|
||||
break;
|
||||
}
|
||||
|
||||
case SpecialType::MerkleUpdate: {
|
||||
if (bits != 8 + (hash_bytes + depth_bytes) * 8 * 2) {
|
||||
return td::Status::Error("Not enouch data for a MerkleUpdate special cell");
|
||||
}
|
||||
if (refs.size() != 2) {
|
||||
return td::Status::Error("Wrong references count for a MerkleUpdate special cell");
|
||||
}
|
||||
if (td::bitstring::bits_memcmp(data + 8, refs[0]->get_hash(0).as_bitslice().get_ptr(), hash_bits) != 0) {
|
||||
return td::Status::Error("First hash mismatch in a MerkleProof special cell");
|
||||
}
|
||||
if (td::bitstring::bits_memcmp(data + 8 + hash_bits, refs[1]->get_hash(0).as_bitslice().get_ptr(), hash_bits) !=
|
||||
0) {
|
||||
return td::Status::Error("Second hash mismatch in a MerkleProof special cell");
|
||||
}
|
||||
if (td::bitstring::bits_load_ulong(data + 8 + 2 * hash_bits, depth_bytes * 8) != refs[0]->get_depth(0)) {
|
||||
return td::Status::Error("First depth mismatch in a MerkleProof special cell");
|
||||
}
|
||||
if (td::bitstring::bits_load_ulong(data + 8 + 2 * hash_bits + depth_bytes * 8, depth_bytes * 8) !=
|
||||
refs[1]->get_depth(0)) {
|
||||
return td::Status::Error("Second depth mismatch in a MerkleProof special cell");
|
||||
}
|
||||
|
||||
level_mask = refs[0]->get_level_mask().apply_or(refs[1]->get_level_mask()).shift_right();
|
||||
virtualization = td::max(refs[0]->get_virtualization(), refs[1]->get_virtualization());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return td::Status::Error("Unknown special cell type");
|
||||
}
|
||||
|
||||
Info info;
|
||||
if (td::unlikely(bits > max_bits)) {
|
||||
return td::Status::Error("Too many bits");
|
||||
}
|
||||
if (td::unlikely(refs.size() > max_refs)) {
|
||||
return td::Status::Error("Too many cell references");
|
||||
}
|
||||
if (td::unlikely(virtualization > max_virtualization)) {
|
||||
return td::Status::Error("Too big virtualization");
|
||||
}
|
||||
|
||||
CHECK(level_mask.get_level() <= max_level);
|
||||
|
||||
auto hash_count = type == SpecialType::PrunnedBranch ? 1 : level_mask.get_hashes_count();
|
||||
DCHECK(hash_count <= max_level + 1);
|
||||
|
||||
info.bits_ = bits;
|
||||
info.refs_count_ = refs.size() & 7;
|
||||
info.is_special_ = special;
|
||||
info.level_mask_ = level_mask.get_mask() & 7;
|
||||
info.hash_count_ = hash_count & 7;
|
||||
info.virtualization_ = virtualization & 7;
|
||||
|
||||
auto data_cell = create_empty_data_cell(info);
|
||||
auto* storage = data_cell->get_storage();
|
||||
|
||||
// init data
|
||||
auto* data_ptr = info.get_data(storage);
|
||||
td::BitPtr{data_ptr}.copy_from(data, bits);
|
||||
// prepare for serialization
|
||||
if (bits & 7) {
|
||||
int m = (0x80 >> (bits & 7));
|
||||
unsigned l = bits / 8;
|
||||
data_ptr[l] = static_cast<unsigned char>((data_ptr[l] & -m) | m);
|
||||
}
|
||||
|
||||
// init refs
|
||||
auto refs_ptr = info.get_refs(storage);
|
||||
for (size_t i = 0; i < refs.size(); i++) {
|
||||
refs_ptr[i] = refs[i].release();
|
||||
}
|
||||
|
||||
// init hashes and depth
|
||||
auto* hashes_ptr = info.get_hashes(storage);
|
||||
auto* depth_ptr = info.get_depth(storage);
|
||||
|
||||
// NB: be careful with special cells
|
||||
auto total_hash_count = level_mask.get_hashes_count();
|
||||
auto hash_i_offset = total_hash_count - hash_count;
|
||||
for (td::uint32 level_i = 0, hash_i = 0, level = level_mask.get_level(); level_i <= level; level_i++) {
|
||||
if (!level_mask.is_significant(level_i)) {
|
||||
continue;
|
||||
}
|
||||
SCOPE_EXIT {
|
||||
hash_i++;
|
||||
};
|
||||
if (hash_i < hash_i_offset) {
|
||||
continue;
|
||||
}
|
||||
unsigned char tmp[2];
|
||||
tmp[0] = info.d1(level_mask.apply(level_i));
|
||||
tmp[1] = info.d2();
|
||||
|
||||
static TD_THREAD_LOCAL digest::SHA256* hasher;
|
||||
td::init_thread_local<digest::SHA256>(hasher);
|
||||
hasher->reset();
|
||||
|
||||
hasher->feed(td::Slice(tmp, 2));
|
||||
|
||||
if (hash_i == hash_i_offset) {
|
||||
DCHECK(level_i == 0 || type == SpecialType::PrunnedBranch);
|
||||
hasher->feed(td::Slice(data_ptr, (bits + 7) >> 3));
|
||||
} else {
|
||||
DCHECK(level_i != 0 && type != SpecialType::PrunnedBranch);
|
||||
hasher->feed(hashes_ptr[hash_i - hash_i_offset - 1].as_slice());
|
||||
}
|
||||
|
||||
auto dest_i = hash_i - hash_i_offset;
|
||||
|
||||
// calc depth
|
||||
td::uint16 depth = 0;
|
||||
for (int i = 0; i < info.refs_count_; i++) {
|
||||
td::uint16 child_depth = 0;
|
||||
if (type == SpecialType::MerkleProof || type == SpecialType::MerkleUpdate) {
|
||||
child_depth = refs_ptr[i]->get_depth(level_i + 1);
|
||||
} else {
|
||||
child_depth = refs_ptr[i]->get_depth(level_i);
|
||||
}
|
||||
|
||||
// add depth into hash
|
||||
td::uint8 child_depth_buf[depth_bytes];
|
||||
store_depth(child_depth_buf, child_depth);
|
||||
hasher->feed(td::Slice(child_depth_buf, depth_bytes));
|
||||
|
||||
depth = std::max(depth, child_depth);
|
||||
}
|
||||
if (info.refs_count_ != 0) {
|
||||
if (depth >= max_depth) {
|
||||
return td::Status::Error("Depth is too big");
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
depth_ptr[dest_i] = depth;
|
||||
|
||||
// children hash
|
||||
for (int i = 0; i < info.refs_count_; i++) {
|
||||
if (type == SpecialType::MerkleProof || type == SpecialType::MerkleUpdate) {
|
||||
hasher->feed(refs_ptr[i]->get_hash(level_i + 1).as_slice());
|
||||
} else {
|
||||
hasher->feed(refs_ptr[i]->get_hash(level_i).as_slice());
|
||||
}
|
||||
}
|
||||
auto extracted_size = hasher->extract(hashes_ptr[dest_i].as_slice());
|
||||
DCHECK(extracted_size == hash_bytes);
|
||||
}
|
||||
|
||||
return Ref<DataCell>(data_cell.release(), Ref<DataCell>::acquire_t{});
|
||||
}
|
||||
|
||||
const DataCell::Hash DataCell::do_get_hash(td::uint32 level) const {
|
||||
auto hash_i = get_level_mask().apply(level).get_hash_i();
|
||||
if (special_type() == SpecialType::PrunnedBranch) {
|
||||
auto this_hash_i = get_level_mask().get_hash_i();
|
||||
if (hash_i != this_hash_i) {
|
||||
return reinterpret_cast<const Hash*>(info_.get_data(get_storage()) + 2)[hash_i];
|
||||
}
|
||||
hash_i = 0;
|
||||
}
|
||||
return info_.get_hashes(get_storage())[hash_i];
|
||||
}
|
||||
|
||||
td::uint16 DataCell::do_get_depth(td::uint32 level) const {
|
||||
auto hash_i = get_level_mask().apply(level).get_hash_i();
|
||||
if (special_type() == SpecialType::PrunnedBranch) {
|
||||
auto this_hash_i = get_level_mask().get_hash_i();
|
||||
if (hash_i != this_hash_i) {
|
||||
return load_depth(info_.get_data(get_storage()) + 2 + hash_bytes * this_hash_i + hash_i * depth_bytes);
|
||||
}
|
||||
hash_i = 0;
|
||||
}
|
||||
return info_.get_depth(get_storage())[hash_i];
|
||||
}
|
||||
|
||||
int DataCell::serialize(unsigned char* buff, int buff_size, bool with_hashes) const {
|
||||
int len = get_serialized_size(with_hashes);
|
||||
if (len > buff_size) {
|
||||
return 0;
|
||||
}
|
||||
buff[0] = static_cast<unsigned char>(info_.d1() | (with_hashes * 16));
|
||||
buff[1] = info_.d2();
|
||||
int hs = 0;
|
||||
if (with_hashes) {
|
||||
hs = (get_level_mask().get_hashes_count()) * (hash_bytes + depth_bytes);
|
||||
assert(len >= 2 + hs);
|
||||
std::memset(buff + 2, 0, hs);
|
||||
auto dest = td::MutableSlice(buff + 2, hs);
|
||||
auto level = get_level();
|
||||
// TODO: optimize for prunned brandh
|
||||
for (unsigned i = 0; i <= level; i++) {
|
||||
if (!get_level_mask().is_significant(i)) {
|
||||
continue;
|
||||
}
|
||||
dest.copy_from(get_hash(i).as_slice());
|
||||
dest.remove_prefix(hash_bytes);
|
||||
}
|
||||
for (unsigned i = 0; i <= level; i++) {
|
||||
if (!get_level_mask().is_significant(i)) {
|
||||
continue;
|
||||
}
|
||||
store_depth(dest.ubegin(), get_depth(i));
|
||||
dest.remove_prefix(depth_bytes);
|
||||
}
|
||||
// buff[2] = 0; // for testing hash verification in deserialization
|
||||
buff += hs;
|
||||
len -= hs;
|
||||
}
|
||||
std::memcpy(buff + 2, get_data(), len - 2);
|
||||
return len + hs;
|
||||
}
|
||||
|
||||
std::string DataCell::serialize() const {
|
||||
unsigned char buff[max_serialized_bytes];
|
||||
int len = serialize(buff, sizeof(buff));
|
||||
return std::string(buff, buff + len);
|
||||
}
|
||||
|
||||
std::string DataCell::to_hex() const {
|
||||
unsigned char buff[max_serialized_bytes];
|
||||
int len = serialize(buff, sizeof(buff));
|
||||
char hex_buff[max_serialized_bytes * 2 + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
sprintf(hex_buff + 2 * i, "%02x", buff[i]);
|
||||
}
|
||||
return hex_buff;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DataCell& c) {
|
||||
return os << c.to_hex();
|
||||
}
|
||||
|
||||
} // namespace vm
|
212
crypto/vm/cells/DataCell.h
Normal file
212
crypto/vm/cells/DataCell.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
|
||||
#include "td/utils/Span.h"
|
||||
|
||||
#include "td/utils/ThreadSafeCounter.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
class DataCell : public Cell {
|
||||
public:
|
||||
DataCell(const DataCell& other) = delete;
|
||||
~DataCell() override;
|
||||
|
||||
static void store_depth(td::uint8* dest, td::uint16 depth) {
|
||||
td::bitstring::bits_store_long(dest, depth, depth_bits);
|
||||
}
|
||||
static td::uint16 load_depth(const td::uint8* src) {
|
||||
return td::bitstring::bits_load_ulong(src, depth_bits) & 0xff;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct Info {
|
||||
unsigned bits_;
|
||||
|
||||
// d1
|
||||
unsigned char refs_count_ : 3;
|
||||
bool is_special_ : 1;
|
||||
unsigned char level_mask_ : 3;
|
||||
|
||||
unsigned char hash_count_ : 3;
|
||||
|
||||
unsigned char virtualization_ : 3;
|
||||
|
||||
unsigned char d1() const {
|
||||
return d1(LevelMask{level_mask_});
|
||||
}
|
||||
unsigned char d1(LevelMask level_mask) const {
|
||||
// d1 = refs_count + 8 * is_special + 32 * level
|
||||
// + 16 * with_hashes - for seriazlization
|
||||
// d1 = 7 + 16 + 32 * l - for absent cells
|
||||
return static_cast<unsigned char>(refs_count_ + 8 * is_special_ + 32 * level_mask.get_mask());
|
||||
}
|
||||
unsigned char d2() const {
|
||||
auto res = static_cast<unsigned char>((bits_ / 8) * 2);
|
||||
if ((bits_ & 7) != 0) {
|
||||
return static_cast<unsigned char>(res + 1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
size_t get_hashes_offset() const {
|
||||
return 0;
|
||||
}
|
||||
size_t get_refs_offset() const {
|
||||
return get_hashes_offset() + hash_bytes * hash_count_;
|
||||
}
|
||||
size_t get_depth_offset() const {
|
||||
return get_refs_offset() + refs_count_ * sizeof(Cell*);
|
||||
}
|
||||
size_t get_data_offset() const {
|
||||
return get_depth_offset() + sizeof(td::uint16) * hash_count_;
|
||||
}
|
||||
size_t get_storage_size() const {
|
||||
return get_data_offset() + (bits_ + 7) / 8;
|
||||
}
|
||||
|
||||
const Hash* get_hashes(const char* storage) const {
|
||||
return reinterpret_cast<const Hash*>(storage + get_hashes_offset());
|
||||
}
|
||||
|
||||
Hash* get_hashes(char* storage) const {
|
||||
return reinterpret_cast<Hash*>(storage + get_hashes_offset());
|
||||
}
|
||||
|
||||
const td::uint16* get_depth(const char* storage) const {
|
||||
return reinterpret_cast<const td::uint16*>(storage + get_depth_offset());
|
||||
}
|
||||
|
||||
td::uint16* get_depth(char* storage) const {
|
||||
return reinterpret_cast<td::uint16*>(storage + get_depth_offset());
|
||||
}
|
||||
|
||||
const unsigned char* get_data(const char* storage) const {
|
||||
return reinterpret_cast<const unsigned char*>(storage + get_data_offset());
|
||||
}
|
||||
unsigned char* get_data(char* storage) const {
|
||||
return reinterpret_cast<unsigned char*>(storage + get_data_offset());
|
||||
}
|
||||
|
||||
Cell* const* get_refs(const char* storage) const {
|
||||
return reinterpret_cast<Cell* const*>(storage + get_refs_offset());
|
||||
}
|
||||
Cell** get_refs(char* storage) const {
|
||||
return reinterpret_cast<Cell**>(storage + get_refs_offset());
|
||||
}
|
||||
};
|
||||
|
||||
Info info_;
|
||||
virtual char* get_storage() = 0;
|
||||
virtual const char* get_storage() const = 0;
|
||||
// TODO: we may also save three different pointers
|
||||
|
||||
void destroy_storage(char* storage);
|
||||
|
||||
explicit DataCell(Info info);
|
||||
Cell* get_ref_raw_ptr(unsigned idx) const {
|
||||
DCHECK(idx < get_refs_cnt());
|
||||
return info_.get_refs(get_storage())[idx];
|
||||
}
|
||||
|
||||
public:
|
||||
td::Result<LoadedCell> load_cell() const override {
|
||||
return LoadedCell{Ref<DataCell>{this}, {}, {}};
|
||||
}
|
||||
unsigned get_refs_cnt() const {
|
||||
return info_.refs_count_;
|
||||
}
|
||||
unsigned get_bits() const {
|
||||
return info_.bits_;
|
||||
}
|
||||
unsigned size_refs() const {
|
||||
return info_.refs_count_;
|
||||
}
|
||||
unsigned size() const {
|
||||
return info_.bits_;
|
||||
}
|
||||
const unsigned char* get_data() const {
|
||||
return info_.get_data(get_storage());
|
||||
}
|
||||
Ref<Cell> get_ref(unsigned idx) const {
|
||||
if (idx >= get_refs_cnt()) {
|
||||
return Ref<Cell>{};
|
||||
}
|
||||
return Ref<Cell>(get_ref_raw_ptr(idx));
|
||||
}
|
||||
|
||||
td::uint32 get_virtualization() const override {
|
||||
return info_.virtualization_;
|
||||
}
|
||||
CellUsageTree::NodePtr get_tree_node() const override {
|
||||
return {};
|
||||
}
|
||||
bool is_loaded() const override {
|
||||
return true;
|
||||
}
|
||||
LevelMask get_level_mask() const override {
|
||||
return LevelMask{info_.level_mask_};
|
||||
}
|
||||
|
||||
bool is_special() const {
|
||||
return info_.is_special_;
|
||||
}
|
||||
SpecialType special_type() const;
|
||||
int get_serialized_size(bool with_hashes = false) const {
|
||||
return ((get_bits() + 23) >> 3) +
|
||||
(with_hashes ? get_level_mask().get_hashes_count() * (hash_bytes + depth_bytes) : 0);
|
||||
}
|
||||
int serialize(unsigned char* buff, int buff_size, bool with_hashes = false) const;
|
||||
std::string serialize() const;
|
||||
std::string to_hex() const;
|
||||
static td::int64 get_total_data_cells() {
|
||||
return get_thread_safe_counter().sum();
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT& storer) const {
|
||||
storer.template store_binary<td::uint8>(info_.d1());
|
||||
storer.template store_binary<td::uint8>(info_.d2());
|
||||
storer.store_slice(td::Slice(get_data(), (get_bits() + 7) / 8));
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr auto max_storage_size = max_refs * sizeof(void*) + (max_level + 1) * hash_bytes + max_bytes;
|
||||
|
||||
private:
|
||||
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() {
|
||||
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("DataCell");
|
||||
return res;
|
||||
}
|
||||
static std::unique_ptr<DataCell> create_empty_data_cell(Info info);
|
||||
|
||||
const Hash do_get_hash(td::uint32 level) const override;
|
||||
td::uint16 do_get_depth(td::uint32 level) const override;
|
||||
|
||||
friend class CellBuilder;
|
||||
static td::Result<Ref<DataCell>> create(td::ConstBitPtr data, unsigned bits, td::Span<Ref<Cell>> refs, bool special);
|
||||
static td::Result<Ref<DataCell>> create(td::ConstBitPtr data, unsigned bits, td::MutableSpan<Ref<Cell>> refs,
|
||||
bool special);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DataCell& c);
|
||||
|
||||
} // namespace vm
|
||||
|
140
crypto/vm/cells/ExtCell.h
Normal file
140
crypto/vm/cells/ExtCell.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
#include "vm/cells/PrunnedCell.h"
|
||||
#include "common/AtomicRef.h"
|
||||
|
||||
#include <mutex>
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/HazardPointers.h"
|
||||
#include "td/utils/optional.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
template <class ExtraT, class Loader>
|
||||
class ExtCell : public Cell {
|
||||
private:
|
||||
struct PrivateTag {};
|
||||
|
||||
public:
|
||||
static td::Result<Ref<ExtCell<ExtraT, Loader>>> create(const PrunnedCellInfo& prunned_cell_info, ExtraT&& extra) {
|
||||
TRY_RESULT(prunned_cell, PrunnedCell<ExtraT>::create(prunned_cell_info, std::move(extra)));
|
||||
return Ref<ExtCell<ExtraT, Loader>>(true, std::move(prunned_cell), PrivateTag{});
|
||||
}
|
||||
|
||||
ExtCell(Ref<PrunnedCell<ExtraT>> prunned_cell, PrivateTag) : prunned_cell_(std::move(prunned_cell)) {
|
||||
get_thread_safe_counter().add(1);
|
||||
get_thread_safe_counter_unloaded().add(prunned_cell_.load_unsafe().not_null());
|
||||
}
|
||||
~ExtCell() {
|
||||
get_thread_safe_counter().add(-1);
|
||||
get_thread_safe_counter_unloaded().add(-static_cast<int>(prunned_cell_.load_unsafe().not_null()));
|
||||
}
|
||||
|
||||
LevelMask get_level_mask() const override {
|
||||
return CellView(this)->get_level_mask();
|
||||
}
|
||||
|
||||
td::Result<LoadedCell> load_cell() const override {
|
||||
TRY_RESULT(data_cell, load_data_cell());
|
||||
return LoadedCell{std::move(data_cell), {}, {}};
|
||||
}
|
||||
td::uint32 get_virtualization() const override {
|
||||
return 0;
|
||||
}
|
||||
CellUsageTree::NodePtr get_tree_node() const override {
|
||||
return {};
|
||||
}
|
||||
bool is_loaded() const override {
|
||||
return CellView(this)->is_loaded();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable td::AtomicRef<DataCell> data_cell_;
|
||||
mutable td::AtomicRef<PrunnedCell<ExtraT>> prunned_cell_;
|
||||
|
||||
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() {
|
||||
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("ExtCell");
|
||||
return res;
|
||||
}
|
||||
|
||||
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter_unloaded() {
|
||||
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("ExtCell.unloaded");
|
||||
return res;
|
||||
}
|
||||
|
||||
struct CellView {
|
||||
CellView(const ExtCell<ExtraT, Loader>* cell) {
|
||||
cell_ = cell->data_cell_.get_unsafe();
|
||||
if (cell_) {
|
||||
return;
|
||||
}
|
||||
|
||||
prunned_cell_ = cell->prunned_cell_.load();
|
||||
if (!prunned_cell_.is_null()) {
|
||||
cell_ = &*prunned_cell_;
|
||||
return;
|
||||
}
|
||||
cell_ = cell->data_cell_.get_unsafe();
|
||||
DCHECK(cell_);
|
||||
}
|
||||
|
||||
const Cell* operator->() const {
|
||||
return cell_;
|
||||
}
|
||||
|
||||
td::Ref<PrunnedCell<ExtraT>> prunned_cell_;
|
||||
const Cell* cell_;
|
||||
};
|
||||
|
||||
const Hash do_get_hash(td::uint32 level) const override {
|
||||
return CellView(this)->get_hash(level);
|
||||
}
|
||||
|
||||
td::uint16 do_get_depth(td::uint32 level) const override {
|
||||
return CellView(this)->get_depth(level);
|
||||
}
|
||||
|
||||
td::Result<Ref<DataCell>> load_data_cell() const {
|
||||
auto data_cell = data_cell_.get_unsafe();
|
||||
if (data_cell) {
|
||||
return Ref<DataCell>(data_cell);
|
||||
}
|
||||
|
||||
auto prunned_cell = prunned_cell_.load();
|
||||
|
||||
if (prunned_cell.is_null()) {
|
||||
data_cell = data_cell_.get_unsafe();
|
||||
DCHECK(data_cell);
|
||||
return Ref<DataCell>(data_cell);
|
||||
}
|
||||
|
||||
TRY_RESULT(new_data_cell, Loader::load_data_cell(*this, prunned_cell->get_extra()));
|
||||
TRY_STATUS(prunned_cell->check_equals_unloaded(new_data_cell));
|
||||
|
||||
if (data_cell_.store_if_empty(new_data_cell)) {
|
||||
prunned_cell_.store({});
|
||||
get_thread_safe_counter_unloaded().add(-1);
|
||||
}
|
||||
|
||||
return data_cell_.load_unsafe();
|
||||
}
|
||||
};
|
||||
} // namespace vm
|
32
crypto/vm/cells/LevelMask.cpp
Normal file
32
crypto/vm/cells/LevelMask.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "LevelMask.h"
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, LevelMask level_mask) {
|
||||
sb << "LevelMask{";
|
||||
for (int i = 0, level = level_mask.get_level(); i < level; i++) {
|
||||
sb << "01"[(level_mask.get_mask() >> i) % 2];
|
||||
}
|
||||
sb << "}";
|
||||
return sb;
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace vm
|
84
crypto/vm/cells/LevelMask.h
Normal file
84
crypto/vm/cells/LevelMask.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/int_types.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
namespace td {
|
||||
class StringBuilder;
|
||||
}
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
class LevelMask {
|
||||
public:
|
||||
explicit LevelMask(td::uint32 new_mask = 0) : mask_(new_mask) {
|
||||
}
|
||||
td::uint32 get_mask() const {
|
||||
return mask_;
|
||||
}
|
||||
td::uint32 get_level() const {
|
||||
return 32 - td::count_leading_zeroes32(mask_);
|
||||
}
|
||||
td::uint32 get_hash_i() const {
|
||||
return td::count_bits32(mask_);
|
||||
}
|
||||
td::uint32 get_hashes_count() const {
|
||||
return get_hash_i() + 1;
|
||||
}
|
||||
LevelMask apply(td::uint32 level) const {
|
||||
DCHECK(level < 32);
|
||||
return LevelMask{mask_ & ((1u << level) - 1)};
|
||||
}
|
||||
LevelMask apply_or(LevelMask other) const {
|
||||
return LevelMask{mask_ | other.mask_};
|
||||
}
|
||||
LevelMask shift_right() const {
|
||||
return LevelMask{mask_ >> 1};
|
||||
}
|
||||
bool is_significant(td::uint32 level) const {
|
||||
DCHECK(level < 32);
|
||||
bool res = level == 0 || ((mask_ >> (level - 1)) % 2 != 0);
|
||||
CHECK(res == (apply(level).get_level() == level));
|
||||
return res;
|
||||
}
|
||||
|
||||
bool operator==(const LevelMask& other) const {
|
||||
return mask_ == other.mask_;
|
||||
}
|
||||
bool operator!=(const LevelMask& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static LevelMask one_level(td::uint32 level) {
|
||||
DCHECK(level < 32);
|
||||
if (level == 0) {
|
||||
return LevelMask(0);
|
||||
}
|
||||
return LevelMask(1 << (level - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
td::uint32 mask_;
|
||||
};
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, LevelMask level_mask);
|
||||
} // namespace detail
|
||||
} // namespace vm
|
263
crypto/vm/cells/MerkleProof.cpp
Normal file
263
crypto/vm/cells/MerkleProof.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "vm/cells/CellBuilder.h"
|
||||
#include "vm/cells/CellSlice.h"
|
||||
|
||||
#include "td/utils/HashMap.h"
|
||||
#include "td/utils/HashSet.h"
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
class MerkleProofImpl {
|
||||
public:
|
||||
explicit MerkleProofImpl(MerkleProof::IsPrunnedFunction is_prunned) : is_prunned_(std::move(is_prunned)) {
|
||||
}
|
||||
explicit MerkleProofImpl(CellUsageTree *usage_tree) : usage_tree_(usage_tree) {
|
||||
}
|
||||
|
||||
Ref<Cell> create_from(Ref<Cell> cell) {
|
||||
if (!is_prunned_) {
|
||||
CHECK(usage_tree_);
|
||||
dfs_usage_tree(cell, usage_tree_->root_id());
|
||||
is_prunned_ = [this](const Ref<Cell> &cell) { return visited_cells_.count(cell->get_hash()) == 0; };
|
||||
}
|
||||
return dfs(cell, cell->get_level());
|
||||
}
|
||||
|
||||
private:
|
||||
using Key = std::pair<Cell::Hash, int>;
|
||||
td::HashMap<Key, Ref<Cell>> cells_;
|
||||
td::HashSet<Cell::Hash> visited_cells_;
|
||||
CellUsageTree *usage_tree_{nullptr};
|
||||
MerkleProof::IsPrunnedFunction is_prunned_;
|
||||
|
||||
void dfs_usage_tree(Ref<Cell> cell, CellUsageTree::NodeId node_id) {
|
||||
if (!usage_tree_->is_loaded(node_id)) {
|
||||
return;
|
||||
}
|
||||
visited_cells_.insert(cell->get_hash());
|
||||
CellSlice cs(NoVm(), cell);
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
dfs_usage_tree(cs.prefetch_ref(i), usage_tree_->get_child(node_id, i));
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Cell> dfs(Ref<Cell> cell, int merkle_depth) {
|
||||
CHECK(cell.not_null());
|
||||
Key key{cell->get_hash(), merkle_depth};
|
||||
{
|
||||
auto it = cells_.find(key);
|
||||
if (it != cells_.end()) {
|
||||
CHECK(it->second.not_null());
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prunned_(cell)) {
|
||||
auto res = CellBuilder::create_pruned_branch(cell, merkle_depth + 1);
|
||||
CHECK(res.not_null());
|
||||
cells_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
CellSlice cs(NoVm(), cell);
|
||||
int children_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
cb.store_ref(dfs(cs.prefetch_ref(i), children_merkle_depth));
|
||||
}
|
||||
auto res = cb.finalize(cs.is_special());
|
||||
CHECK(res.not_null());
|
||||
cells_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
Ref<Cell> MerkleProof::generate_raw(Ref<Cell> cell, IsPrunnedFunction is_prunned) {
|
||||
return detail::MerkleProofImpl(is_prunned).create_from(cell);
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::generate_raw(Ref<Cell> cell, CellUsageTree *usage_tree) {
|
||||
return detail::MerkleProofImpl(usage_tree).create_from(cell);
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::virtualize_raw(Ref<Cell> cell, Cell::VirtualizationParameters virt) {
|
||||
return cell->virtualize(virt);
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::generate(Ref<Cell> cell, IsPrunnedFunction is_prunned) {
|
||||
int cell_level = cell->get_level();
|
||||
if (cell_level != 0) {
|
||||
return {};
|
||||
}
|
||||
auto raw = generate_raw(std::move(cell), is_prunned);
|
||||
return CellBuilder::create_merkle_proof(std::move(raw));
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::generate(Ref<Cell> cell, CellUsageTree *usage_tree) {
|
||||
int cell_level = cell->get_level();
|
||||
if (cell_level != 0) {
|
||||
return {};
|
||||
}
|
||||
auto raw = generate_raw(std::move(cell), usage_tree);
|
||||
return CellBuilder::create_merkle_proof(std::move(raw));
|
||||
}
|
||||
|
||||
td::Result<Ref<Cell>> unpack_proof(Ref<Cell> cell) {
|
||||
CHECK(cell.not_null());
|
||||
td::uint8 level = static_cast<td::uint8>(cell->get_level());
|
||||
if (level != 0) {
|
||||
return td::Status::Error("Level of MerkleProof must be zero");
|
||||
}
|
||||
CellSlice cs(NoVm(), std::move(cell));
|
||||
if (cs.special_type() != Cell::SpecialType::MerkleProof) {
|
||||
return td::Status::Error("Not a MekleProof cell");
|
||||
}
|
||||
return cs.fetch_ref();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::virtualize(Ref<Cell> cell, int virtualization) {
|
||||
auto r_raw = unpack_proof(std::move(cell));
|
||||
if (r_raw.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return virtualize_raw(r_raw.move_as_ok(), {0 /*level*/, static_cast<td::uint8>(virtualization)});
|
||||
}
|
||||
|
||||
class MerkleProofCombine {
|
||||
public:
|
||||
MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
|
||||
}
|
||||
td::Result<Ref<Cell>> run() {
|
||||
TRY_RESULT(a, unpack_proof(a_));
|
||||
TRY_RESULT(b, unpack_proof(b_));
|
||||
if (a->get_hash(0) != b->get_hash(0)) {
|
||||
return td::Status::Error("Can't combine MerkleProofs with different roots");
|
||||
}
|
||||
dfs(a, 0);
|
||||
dfs(b, 0);
|
||||
return CellBuilder::create_merkle_proof(create_A(a, 0, 0));
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Cell> a_;
|
||||
Ref<Cell> b_;
|
||||
|
||||
struct Info {
|
||||
Ref<Cell> cell_;
|
||||
Ref<Cell> prunned_cells_[Cell::max_level]; // Cache prunned cells with different levels to reuse them
|
||||
|
||||
Ref<Cell> get_prunned_cell(int depth) {
|
||||
if (depth < Cell::max_level) {
|
||||
return prunned_cells_[depth];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
Ref<Cell> get_any_cell() const {
|
||||
if (cell_.not_null()) {
|
||||
return cell_;
|
||||
}
|
||||
for (auto &cell : prunned_cells_) {
|
||||
if (cell.not_null()) {
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
using Key = std::pair<Cell::Hash, int>;
|
||||
td::HashMap<Cell::Hash, Info> cells_;
|
||||
td::HashMap<Key, Ref<Cell>> create_A_res_;
|
||||
td::HashSet<Key> visited_;
|
||||
|
||||
void dfs(Ref<Cell> cell, int merkle_depth) {
|
||||
if (!visited_.emplace(cell->get_hash(), merkle_depth).second) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
CellSlice cs(NoVm(), cell);
|
||||
// check if prunned cell is bounded
|
||||
if (cs.special_type() == Cell::SpecialType::PrunnedBranch && static_cast<int>(cell->get_level()) > merkle_depth) {
|
||||
info.prunned_cells_[cell->get_level() - 1] = std::move(cell);
|
||||
return;
|
||||
}
|
||||
info.cell_ = std::move(cell);
|
||||
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
for (size_t i = 0, size = cs.size_refs(); i < size; i++) {
|
||||
dfs(cs.fetch_ref(), child_merkle_depth);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Cell> create_A(Ref<Cell> cell, int merkle_depth, int a_merkle_depth) {
|
||||
merkle_depth = cell->get_level_mask().apply(merkle_depth).get_level();
|
||||
auto key = Key(cell->get_hash(merkle_depth), a_merkle_depth);
|
||||
auto it = create_A_res_.find(key);
|
||||
if (it != create_A_res_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto res = do_create_A(std::move(cell), merkle_depth, a_merkle_depth);
|
||||
create_A_res_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Ref<Cell> do_create_A(Ref<Cell> cell, int merkle_depth, int a_merkle_depth) {
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
|
||||
if (info.cell_.is_null()) {
|
||||
Ref<Cell> res = info.get_prunned_cell(a_merkle_depth);
|
||||
if (res.is_null()) {
|
||||
res = CellBuilder::create_pruned_branch(info.get_any_cell(), a_merkle_depth + 1, merkle_depth);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
CHECK(info.cell_.not_null());
|
||||
CellSlice cs(NoVm(), info.cell_);
|
||||
|
||||
//CHECK(cs.size_refs() != 0);
|
||||
if (cs.size_refs() == 0) {
|
||||
return info.cell_;
|
||||
}
|
||||
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
auto child_a_merkle_depth = cs.child_merkle_depth(a_merkle_depth);
|
||||
|
||||
CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
cb.store_ref(create_A(cs.prefetch_ref(i), child_merkle_depth, child_a_merkle_depth));
|
||||
}
|
||||
return cb.finalize(cs.is_special());
|
||||
}
|
||||
};
|
||||
|
||||
Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
|
||||
auto res = MerkleProofCombine(std::move(a), std::move(b)).run();
|
||||
if (res.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
} // namespace vm
|
46
crypto/vm/cells/MerkleProof.h
Normal file
46
crypto/vm/cells/MerkleProof.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
namespace vm {
|
||||
class MerkleProof {
|
||||
public:
|
||||
using IsPrunnedFunction = std::function<bool(const Ref<Cell> &)>;
|
||||
|
||||
// works with proofs wrapped in MerkleProof special cell
|
||||
// cells must have zero level
|
||||
static Ref<Cell> generate(Ref<Cell> cell, IsPrunnedFunction is_prunned);
|
||||
static Ref<Cell> generate(Ref<Cell> cell, CellUsageTree *usage_tree);
|
||||
|
||||
// cell must have zero level and must be a MerkleProof
|
||||
static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization);
|
||||
|
||||
static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b);
|
||||
|
||||
// works with upwrapped proofs
|
||||
// works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell
|
||||
static Ref<Cell> generate_raw(Ref<Cell> cell, IsPrunnedFunction is_prunned);
|
||||
static Ref<Cell> generate_raw(Ref<Cell> cell, CellUsageTree *usage_tree);
|
||||
static Ref<Cell> virtualize_raw(Ref<Cell> cell, Cell::VirtualizationParameters virt);
|
||||
};
|
||||
} // namespace vm
|
510
crypto/vm/cells/MerkleUpdate.cpp
Normal file
510
crypto/vm/cells/MerkleUpdate.cpp
Normal file
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "vm/cells/MerkleUpdate.h"
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
|
||||
#include "td/utils/HashMap.h"
|
||||
#include "td/utils/HashSet.h"
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
class MerkleUpdateApply {
|
||||
public:
|
||||
Ref<Cell> apply(Ref<Cell> from, Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level,
|
||||
td::uint32 to_level) {
|
||||
if (from_level != from->get_level()) {
|
||||
return {};
|
||||
}
|
||||
dfs_both(from, update_from, from_level);
|
||||
return dfs(update_to, to_level);
|
||||
}
|
||||
|
||||
private:
|
||||
using Key = std::pair<Cell::Hash, int>;
|
||||
td::HashMap<Cell::Hash, Ref<Cell>> known_cells_;
|
||||
td::HashMap<Key, Ref<Cell>> ready_cells_;
|
||||
|
||||
void dfs_both(Ref<Cell> original, Ref<Cell> update_from, int merkle_depth) {
|
||||
CellSlice cs_update_from(NoVm(), update_from);
|
||||
known_cells_.emplace(original->get_hash(merkle_depth), original);
|
||||
if (cs_update_from.special_type() == Cell::SpecialType::PrunnedBranch) {
|
||||
return;
|
||||
}
|
||||
int child_merkle_depth = cs_update_from.child_merkle_depth(merkle_depth);
|
||||
|
||||
CellSlice cs_original(NoVm(), original);
|
||||
for (unsigned i = 0; i < cs_original.size_refs(); i++) {
|
||||
dfs_both(cs_original.prefetch_ref(i), cs_update_from.prefetch_ref(i), child_merkle_depth);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Cell> dfs(Ref<Cell> cell, int merkle_depth) {
|
||||
CellSlice cs(NoVm(), cell);
|
||||
if (cs.special_type() == Cell::SpecialType::PrunnedBranch) {
|
||||
if ((int)cell->get_level() == merkle_depth + 1) {
|
||||
auto it = known_cells_.find(cell->get_hash(merkle_depth));
|
||||
if (it != known_cells_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
Key key{cell->get_hash(), merkle_depth};
|
||||
{
|
||||
auto it = ready_cells_.find(key);
|
||||
if (it != ready_cells_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
int child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
|
||||
CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
auto ref = dfs(cs.prefetch_ref(i), child_merkle_depth);
|
||||
if (ref.is_null()) {
|
||||
return {};
|
||||
}
|
||||
cb.store_ref(std::move(ref));
|
||||
}
|
||||
auto res = cb.finalize(cs.is_special());
|
||||
ready_cells_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class MerkleUpdateValidator {
|
||||
public:
|
||||
td::Status validate(Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level, td::uint32 to_level) {
|
||||
dfs_from(update_from, from_level);
|
||||
return dfs_to(update_to, to_level);
|
||||
}
|
||||
|
||||
private:
|
||||
td::HashSet<Cell::Hash> known_cells_;
|
||||
using Key = std::pair<Cell::Hash, int>;
|
||||
td::HashSet<Key> visited_from_;
|
||||
td::HashSet<Key> visited_to_;
|
||||
|
||||
void dfs_from(Ref<Cell> cell, int merkle_depth) {
|
||||
if (!visited_from_.emplace(cell->get_hash(), merkle_depth).second) {
|
||||
return;
|
||||
}
|
||||
CellSlice cs(NoVm(), cell);
|
||||
known_cells_.insert(cell->get_hash(merkle_depth));
|
||||
if (cs.special_type() == Cell::SpecialType::PrunnedBranch) {
|
||||
return;
|
||||
}
|
||||
int child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
dfs_from(cs.prefetch_ref(i), child_merkle_depth);
|
||||
}
|
||||
}
|
||||
|
||||
td::Status dfs_to(Ref<Cell> cell, int merkle_depth) {
|
||||
if (!visited_to_.emplace(cell->get_hash(), merkle_depth).second) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
CellSlice cs(NoVm(), cell);
|
||||
if (cs.special_type() == Cell::SpecialType::PrunnedBranch) {
|
||||
if ((int)cell->get_level() == merkle_depth + 1) {
|
||||
if (known_cells_.count(cell->get_hash(merkle_depth)) == 0) {
|
||||
return td::Status::Error(PSLICE()
|
||||
<< "Unknown prunned cell (validate): " << cell->get_hash(merkle_depth).to_hex());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
int child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
TRY_STATUS(dfs_to(cs.prefetch_ref(i), child_merkle_depth));
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
td::Status MerkleUpdate::may_apply(Ref<Cell> from, Ref<Cell> update) {
|
||||
if (update->get_level() != 0 || from->get_level() != 0) {
|
||||
return td::Status::Error("Level of update of from is not zero");
|
||||
}
|
||||
CellSlice cs(NoVm(), std::move(update));
|
||||
if (cs.special_type() != Cell::SpecialType::MerkleUpdate) {
|
||||
return td::Status::Error("Update cell is not a MerkeUpdate");
|
||||
}
|
||||
auto update_from = cs.fetch_ref();
|
||||
if (from->get_hash(0) != update_from->get_hash(0)) {
|
||||
return td::Status::Error("Hash mismatch");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleUpdate::apply(Ref<Cell> from, Ref<Cell> update) {
|
||||
if (update->get_level() != 0 || from->get_level() != 0) {
|
||||
return {};
|
||||
}
|
||||
CellSlice cs(NoVm(), std::move(update));
|
||||
if (cs.special_type() != Cell::SpecialType::MerkleUpdate) {
|
||||
return {};
|
||||
}
|
||||
auto update_from = cs.fetch_ref();
|
||||
auto update_to = cs.fetch_ref();
|
||||
return apply_raw(std::move(from), std::move(update_from), std::move(update_to), 0, 0);
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleUpdate::apply_raw(Ref<Cell> from, Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level,
|
||||
td::uint32 to_level) {
|
||||
if (from->get_hash(from_level) != update_from->get_hash(from_level)) {
|
||||
LOG(DEBUG) << "invalid Merkle update: expected old value hash = " << update_from->get_hash(from_level).to_hex()
|
||||
<< ", applied to value with hash = " << from->get_hash(from_level).to_hex();
|
||||
return {};
|
||||
}
|
||||
return detail::MerkleUpdateApply().apply(from, std::move(update_from), std::move(update_to), from_level, to_level);
|
||||
}
|
||||
|
||||
std::pair<Ref<Cell>, Ref<Cell>> MerkleUpdate::generate_raw(Ref<Cell> from, Ref<Cell> to, CellUsageTree *usage_tree) {
|
||||
// create Merkle update cell->new_cell
|
||||
auto update_to = MerkleProof::generate_raw(to, [tree = usage_tree](const Ref<Cell> &cell) {
|
||||
auto loaded_cell = cell->load_cell().move_as_ok(); // FIXME
|
||||
if (loaded_cell.data_cell->size_refs() == 0) {
|
||||
return false;
|
||||
}
|
||||
return !loaded_cell.tree_node.empty() && loaded_cell.tree_node.mark_path(tree);
|
||||
});
|
||||
usage_tree->set_use_mark_for_is_loaded(true);
|
||||
auto update_from = MerkleProof::generate_raw(from, usage_tree);
|
||||
|
||||
return {std::move(update_from), std::move(update_to)};
|
||||
}
|
||||
|
||||
td::Status MerkleUpdate::validate_raw(Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level,
|
||||
td::uint32 to_level) {
|
||||
return detail::MerkleUpdateValidator().validate(std::move(update_from), std::move(update_to), from_level, to_level);
|
||||
}
|
||||
|
||||
td::Status MerkleUpdate::validate(Ref<Cell> update) {
|
||||
if (update->get_level() != 0) {
|
||||
return td::Status::Error("nonzero level");
|
||||
}
|
||||
CellSlice cs(NoVm(), std::move(update));
|
||||
if (cs.special_type() != Cell::SpecialType::MerkleUpdate) {
|
||||
return td::Status::Error("not a MerkleUpdate cell");
|
||||
}
|
||||
auto update_from = cs.fetch_ref();
|
||||
auto update_to = cs.fetch_ref();
|
||||
return validate_raw(std::move(update_from), std::move(update_to), 0, 0);
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleUpdate::generate(Ref<Cell> from, Ref<Cell> to, CellUsageTree *usage_tree) {
|
||||
auto from_level = from->get_level();
|
||||
auto to_level = to->get_level();
|
||||
if (from_level != 0 || to_level != 0) {
|
||||
return {};
|
||||
}
|
||||
auto res = generate_raw(std::move(from), std::move(to), usage_tree);
|
||||
return CellBuilder::create_merkle_update(res.first, res.second);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
class MerkleCombine {
|
||||
public:
|
||||
MerkleCombine(Ref<Cell> AB, Ref<Cell> CD) : AB_(std::move(AB)), CD_(std::move(CD)) {
|
||||
}
|
||||
|
||||
td::Result<Ref<Cell>> run() {
|
||||
TRY_RESULT(AB, unpack_update(std::move(AB_)));
|
||||
TRY_RESULT(CD, unpack_update(std::move(CD_)));
|
||||
std::tie(A_, B_) = AB;
|
||||
std::tie(C_, D_) = CD;
|
||||
if (B_->get_hash(0) != C_->get_hash(0)) {
|
||||
return td::Status::Error("Impossible to combine merkle updates");
|
||||
}
|
||||
|
||||
auto log = [](td::Slice name, auto cell) {
|
||||
CellSlice cs(NoVm(), cell);
|
||||
LOG(ERROR) << name << " " << cell->get_level();
|
||||
cs.print_rec(std::cerr);
|
||||
};
|
||||
if (0) {
|
||||
log("A", A_);
|
||||
log("B", B_);
|
||||
log("C", C_);
|
||||
log("D", D_);
|
||||
}
|
||||
|
||||
// We have four bags of cells. A, B, C and D.
|
||||
// X = Virtualize(A), A is subtree (merkle proof) of X
|
||||
// Y = Virtualize(B) = Virtualize(C), B and C are subrees of Y
|
||||
// Z = Virtualize(D), D is subtree of Z
|
||||
//
|
||||
// Prunned cells bounded by merkle proof P are essentially cells which are impossible to load during traversal of Virtualize(P)
|
||||
//
|
||||
// We want to create new_A and new_D
|
||||
// Virtualize(new_A) = X
|
||||
// Virtualize(new_D) = Z
|
||||
// All prunned branches bounded by new_D must be in new_A
|
||||
// i.e. if we have all cells reachable in Virtualize(new_A) we may construct Z from them (and from new_D)
|
||||
//
|
||||
// Main idea is following
|
||||
// 1. Create maximum subtrees of X and Z with all cells in A, B, C and D
|
||||
// Max(V) - such maximum subtree
|
||||
//
|
||||
// 2. Max(A) and Max(D) should be merkle update already. But win want to minimize it
|
||||
// So we cut all branches of Max(D) which are in maxA
|
||||
// When we cut branch q in Max(D) we mark some path to q in Max(A)
|
||||
// Then we cut all branches of Max(A) which are not marked.
|
||||
//
|
||||
// How to create Max(A)?
|
||||
// We just store all cells reachable from A, B, C and D in big cache.
|
||||
// It we reach bounded prunned cell during traversion we may continue traversial with a cell from the cache.
|
||||
//
|
||||
//
|
||||
// 1. load_cells(root) - caches all cell reachable in Virtualize(root);
|
||||
visited_.clear();
|
||||
load_cells(A_, 0);
|
||||
visited_.clear();
|
||||
load_cells(B_, 0);
|
||||
visited_.clear();
|
||||
load_cells(C_, 0);
|
||||
visited_.clear();
|
||||
load_cells(D_, 0);
|
||||
|
||||
// 2. mark_A(A) - Traverse Max(A), but uses all cached cells from step 1. Mark all visited cells
|
||||
A_usage_tree_ = std::make_shared<CellUsageTree>();
|
||||
mark_A(A_, 0, A_usage_tree_->root_id());
|
||||
|
||||
// 3. create_D(D) - create new_D. Traverse Max(D), and stop at marked cells. Mark path in A to marked cells
|
||||
auto new_D = create_D(D_, 0, 0);
|
||||
if (new_D.is_null()) {
|
||||
return td::Status::Error("Failed to combine updates. One of them is probably an invalid update");
|
||||
}
|
||||
|
||||
// 4. create_A(A) - create new_A. Traverse Max(A), and stop at cells not marked at step 3.
|
||||
auto new_A = create_A(A_, 0, 0);
|
||||
if (0) {
|
||||
log("NewD", new_D);
|
||||
}
|
||||
|
||||
return CellBuilder::create_merkle_update(new_A, new_D);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Cell> AB_, CD_;
|
||||
Ref<Cell> A_, B_, C_, D_;
|
||||
|
||||
std::shared_ptr<CellUsageTree> A_usage_tree_;
|
||||
|
||||
struct Info {
|
||||
Ref<Cell> cell_;
|
||||
Ref<Cell> prunned_cells_[Cell::max_level]; // Cache prunned cells with different levels to reuse them
|
||||
CellUsageTree::NodeId A_node_id{0};
|
||||
|
||||
Ref<Cell> get_prunned_cell(int depth) {
|
||||
if (depth < Cell::max_level) {
|
||||
return prunned_cells_[depth];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
Ref<Cell> get_any_cell() const {
|
||||
if (cell_.not_null()) {
|
||||
return cell_;
|
||||
}
|
||||
for (auto &cell : prunned_cells_) {
|
||||
if (cell.not_null()) {
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
using Key = std::pair<Cell::Hash, int>;
|
||||
td::HashMap<Cell::Hash, Info> cells_;
|
||||
td::HashMap<Key, Ref<Cell>> create_A_res_;
|
||||
td::HashMap<Key, Ref<Cell>> create_D_res_;
|
||||
td::HashSet<Key> visited_;
|
||||
|
||||
void load_cells(Ref<Cell> cell, int merkle_depth) {
|
||||
if (!visited_.emplace(cell->get_hash(), merkle_depth).second) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
CellSlice cs(NoVm(), cell);
|
||||
|
||||
// check if prunned cell is bounded
|
||||
if (cs.special_type() == Cell::SpecialType::PrunnedBranch && static_cast<int>(cell->get_level()) > merkle_depth) {
|
||||
info.prunned_cells_[cell->get_level() - 1] = std::move(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
info.cell_ = std::move(cell);
|
||||
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
for (size_t i = 0, size = cs.size_refs(); i < size; i++) {
|
||||
load_cells(cs.fetch_ref(), child_merkle_depth);
|
||||
}
|
||||
}
|
||||
|
||||
void mark_A(Ref<Cell> cell, int merkle_depth, CellUsageTree::NodeId node_id) {
|
||||
CHECK(node_id != 0);
|
||||
|
||||
// cell in cache may be virtualized with different level
|
||||
// so we make merkle_depth as small as possible
|
||||
merkle_depth = cell->get_level_mask().apply(merkle_depth).get_level();
|
||||
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
if (info.A_node_id != 0) {
|
||||
return;
|
||||
}
|
||||
info.A_node_id = node_id;
|
||||
if (info.cell_.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CellSlice cs(NoVm(), info.cell_);
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
for (int i = 0, size = cs.size_refs(); i < size; i++) {
|
||||
mark_A(cs.fetch_ref(), child_merkle_depth, A_usage_tree_->create_child(node_id, i));
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Cell> create_D(Ref<Cell> cell, int merkle_depth, int d_merkle_depth) {
|
||||
merkle_depth = cell->get_level_mask().apply(merkle_depth).get_level();
|
||||
auto key = Key(cell->get_hash(merkle_depth), d_merkle_depth);
|
||||
auto it = create_D_res_.find(key);
|
||||
if (it != create_D_res_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto res = do_create_D(std::move(cell), merkle_depth, d_merkle_depth);
|
||||
if (res.is_null()) {
|
||||
return {};
|
||||
}
|
||||
create_D_res_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Ref<Cell> do_create_D(Ref<Cell> cell, int merkle_depth, int d_merkle_depth) {
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
if (info.A_node_id != 0) {
|
||||
A_usage_tree_->mark_path(info.A_node_id);
|
||||
Ref<Cell> res = info.get_prunned_cell(d_merkle_depth);
|
||||
if (res.is_null()) {
|
||||
res = CellBuilder::create_pruned_branch(info.get_any_cell(), d_merkle_depth + 1, merkle_depth);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
if (info.cell_.is_null()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
CellSlice cs(NoVm(), info.cell_);
|
||||
|
||||
if (cs.size_refs() == 0) {
|
||||
return info.cell_;
|
||||
}
|
||||
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
auto child_d_merkle_depth = cs.child_merkle_depth(d_merkle_depth);
|
||||
|
||||
CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
auto ref = create_D(cs.prefetch_ref(i), child_merkle_depth, child_d_merkle_depth);
|
||||
if (ref.is_null()) {
|
||||
return {};
|
||||
}
|
||||
cb.store_ref(std::move(ref));
|
||||
}
|
||||
return cb.finalize(cs.is_special());
|
||||
}
|
||||
|
||||
Ref<Cell> create_A(Ref<Cell> cell, int merkle_depth, int a_merkle_depth) {
|
||||
merkle_depth = cell->get_level_mask().apply(merkle_depth).get_level();
|
||||
auto key = Key(cell->get_hash(merkle_depth), a_merkle_depth);
|
||||
auto it = create_A_res_.find(key);
|
||||
if (it != create_A_res_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto res = do_create_A(std::move(cell), merkle_depth, a_merkle_depth);
|
||||
create_A_res_.emplace(key, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Ref<Cell> do_create_A(Ref<Cell> cell, int merkle_depth, int a_merkle_depth) {
|
||||
auto &info = cells_[cell->get_hash(merkle_depth)];
|
||||
|
||||
CHECK(info.A_node_id != 0);
|
||||
if (!A_usage_tree_->has_mark(info.A_node_id)) {
|
||||
Ref<Cell> res = info.get_prunned_cell(a_merkle_depth);
|
||||
if (res.is_null()) {
|
||||
res = CellBuilder::create_pruned_branch(info.get_any_cell(), a_merkle_depth + 1, merkle_depth);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
CHECK(info.cell_.not_null());
|
||||
CellSlice cs(NoVm(), info.cell_);
|
||||
|
||||
CHECK(cs.size_refs() != 0);
|
||||
if (cs.size_refs() == 0) {
|
||||
return info.cell_;
|
||||
}
|
||||
|
||||
auto child_merkle_depth = cs.child_merkle_depth(merkle_depth);
|
||||
auto child_a_merkle_depth = cs.child_merkle_depth(a_merkle_depth);
|
||||
|
||||
CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
cb.store_ref(create_A(cs.prefetch_ref(i), child_merkle_depth, child_a_merkle_depth));
|
||||
}
|
||||
return cb.finalize(cs.is_special());
|
||||
}
|
||||
|
||||
td::Result<std::pair<Ref<Cell>, Ref<Cell>>> unpack_update(Ref<Cell> update) const {
|
||||
if (update->get_level() != 0) {
|
||||
return td::Status::Error("level is not zero");
|
||||
}
|
||||
CellSlice cs(NoVm(), std::move(update));
|
||||
if (cs.special_type() != Cell::SpecialType::MerkleUpdate) {
|
||||
return td::Status::Error("Not a Merkle Update cell");
|
||||
}
|
||||
auto update_from = cs.fetch_ref();
|
||||
auto update_to = cs.fetch_ref();
|
||||
return std::make_pair(std::move(update_from), std::move(update_to));
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
Ref<Cell> MerkleUpdate::combine(Ref<Cell> ab, Ref<Cell> bc) {
|
||||
detail::MerkleCombine combine(ab, bc);
|
||||
auto res = combine.run();
|
||||
if (res.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
} // namespace vm
|
50
crypto/vm/cells/MerkleUpdate.h
Normal file
50
crypto/vm/cells/MerkleUpdate.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
#include "vm/cells/CellSlice.h"
|
||||
#include "vm/cells/CellBuilder.h"
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace vm {
|
||||
class MerkleUpdate {
|
||||
public:
|
||||
// from + update == to
|
||||
static Ref<Cell> generate(Ref<Cell> from, Ref<Cell> to, CellUsageTree *usage_tree);
|
||||
// Returns empty Ref<Cell> if something go wrong. If validate(from).is_ok() and may_apply(from, to).is_ok(), then it
|
||||
// must not fail.
|
||||
static Ref<Cell> apply(Ref<Cell> from, Ref<Cell> update);
|
||||
|
||||
// check if update is valid
|
||||
static TD_WARN_UNUSED_RESULT td::Status validate(Ref<Cell> update);
|
||||
// check that hash in from is same as hash stored in update. Do not validate update
|
||||
static TD_WARN_UNUSED_RESULT td::Status may_apply(Ref<Cell> from, Ref<Cell> update);
|
||||
|
||||
static Ref<Cell> apply_raw(Ref<Cell> from, Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level,
|
||||
td::uint32 to_level);
|
||||
static std::pair<Ref<Cell>, Ref<Cell>> generate_raw(Ref<Cell> from, Ref<Cell> to, CellUsageTree *usage_tree);
|
||||
static td::Status validate_raw(Ref<Cell> update_from, Ref<Cell> update_to, td::uint32 from_level,
|
||||
td::uint32 to_level);
|
||||
|
||||
static Ref<Cell> combine(Ref<Cell> ab, Ref<Cell> bc);
|
||||
};
|
||||
} // namespace vm
|
139
crypto/vm/cells/PrunnedCell.h
Normal file
139
crypto/vm/cells/PrunnedCell.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/CellWithStorage.h"
|
||||
#include "vm/cells/Cell.h"
|
||||
|
||||
namespace vm {
|
||||
struct PrunnedCellInfo {
|
||||
Cell::LevelMask level_mask;
|
||||
td::Slice hash;
|
||||
td::Slice depth;
|
||||
};
|
||||
|
||||
template <class ExtraT>
|
||||
class PrunnedCell : public Cell {
|
||||
public:
|
||||
const ExtraT& get_extra() const {
|
||||
return extra_;
|
||||
}
|
||||
|
||||
static td::Result<Ref<PrunnedCell<ExtraT>>> create(const PrunnedCellInfo& prunned_cell_info, ExtraT&& extra) {
|
||||
auto level_mask = prunned_cell_info.level_mask;
|
||||
if (level_mask.get_level() > max_level) {
|
||||
return td::Status::Error("Level is too big");
|
||||
}
|
||||
Info info(level_mask);
|
||||
auto prunned_cell =
|
||||
detail::CellWithUniquePtrStorage<PrunnedCell<ExtraT>>::create(info.get_storage_size(), info, std::move(extra));
|
||||
TRY_STATUS(prunned_cell->init(prunned_cell_info));
|
||||
return Ref<PrunnedCell<ExtraT>>(prunned_cell.release(), typename Ref<PrunnedCell<ExtraT>>::acquire_t{});
|
||||
}
|
||||
|
||||
LevelMask get_level_mask() const override {
|
||||
return LevelMask(info_.level_mask_);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct Info {
|
||||
Info(LevelMask level_mask) {
|
||||
level_mask_ = level_mask.get_mask() & 7;
|
||||
hash_count_ = level_mask.get_hashes_count() & 7;
|
||||
}
|
||||
unsigned char level_mask_ : 3;
|
||||
unsigned char hash_count_ : 3;
|
||||
size_t get_hashes_offset() const {
|
||||
return 0;
|
||||
}
|
||||
size_t get_depth_offset() const {
|
||||
return get_hashes_offset() + hash_bytes * hash_count_;
|
||||
}
|
||||
size_t get_storage_size() const {
|
||||
return get_depth_offset() + sizeof(td::uint16) * hash_count_;
|
||||
}
|
||||
const Hash* get_hashes(const char* storage) const {
|
||||
return reinterpret_cast<const Hash*>(storage + get_hashes_offset());
|
||||
}
|
||||
Hash* get_hashes(char* storage) const {
|
||||
return reinterpret_cast<Hash*>(storage + get_hashes_offset());
|
||||
}
|
||||
const td::uint16* get_depth(const char* storage) const {
|
||||
return reinterpret_cast<const td::uint16*>(storage + get_depth_offset());
|
||||
}
|
||||
td::uint16* get_depth(char* storage) const {
|
||||
return reinterpret_cast<td::uint16*>(storage + get_depth_offset());
|
||||
}
|
||||
};
|
||||
|
||||
Info info_;
|
||||
ExtraT extra_;
|
||||
virtual char* get_storage() = 0;
|
||||
virtual const char* get_storage() const = 0;
|
||||
void destroy_storage(char* storage) {
|
||||
// noop
|
||||
}
|
||||
|
||||
td::Status init(const PrunnedCellInfo& prunned_cell_info) {
|
||||
auto storage = get_storage();
|
||||
auto& new_hash = prunned_cell_info.hash;
|
||||
auto* hash = info_.get_hashes(storage);
|
||||
size_t n = prunned_cell_info.level_mask.get_hashes_count();
|
||||
CHECK(new_hash.size() == n * hash_bytes);
|
||||
for (td::uint32 i = 0; i < n; i++) {
|
||||
hash[i].as_slice().copy_from(new_hash.substr(i * Cell::hash_bytes, Cell::hash_bytes));
|
||||
}
|
||||
|
||||
auto& new_depth = prunned_cell_info.depth;
|
||||
CHECK(new_depth.size() == n * depth_bytes);
|
||||
auto* depth = info_.get_depth(storage);
|
||||
for (td::uint32 i = 0; i < n; i++) {
|
||||
depth[i] = DataCell::load_depth(new_depth.substr(i * Cell::depth_bytes, Cell::depth_bytes).ubegin());
|
||||
if (depth[i] > max_depth) {
|
||||
return td::Status::Error("Depth is too big");
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
explicit PrunnedCell(Info info, ExtraT&& extra) : info_(info), extra_(std::move(extra)) {
|
||||
}
|
||||
td::uint32 get_virtualization() const override {
|
||||
return 0;
|
||||
}
|
||||
CellUsageTree::NodePtr get_tree_node() const override {
|
||||
return {};
|
||||
}
|
||||
bool is_loaded() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const Hash do_get_hash(td::uint32 level) const override {
|
||||
return info_.get_hashes(get_storage())[get_level_mask().apply(level).get_hash_i()];
|
||||
}
|
||||
|
||||
td::uint16 do_get_depth(td::uint32 level) const override {
|
||||
return info_.get_depth(get_storage())[get_level_mask().apply(level).get_hash_i()];
|
||||
}
|
||||
|
||||
td::Result<LoadedCell> load_cell() const override {
|
||||
return td::Status::Error("Can't load prunned branch");
|
||||
}
|
||||
};
|
||||
} // namespace vm
|
88
crypto/vm/cells/UsageCell.h
Normal file
88
crypto/vm/cells/UsageCell.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
#include "vm/cells/CellUsageTree.h"
|
||||
|
||||
namespace vm {
|
||||
class UsageCell : public Cell {
|
||||
private:
|
||||
struct PrivateTag {};
|
||||
|
||||
public:
|
||||
UsageCell(Ref<Cell> cell, CellUsageTree::NodePtr tree_node, PrivateTag)
|
||||
: cell_(std::move(cell)), tree_node_(std::move(tree_node)) {
|
||||
}
|
||||
static Ref<Cell> create(Ref<Cell> cell, CellUsageTree::NodePtr tree_node) {
|
||||
if (tree_node.empty()) {
|
||||
return cell;
|
||||
}
|
||||
return Ref<UsageCell>{true, std::move(cell), std::move(tree_node), PrivateTag{}};
|
||||
}
|
||||
|
||||
// load interface
|
||||
td::Result<LoadedCell> load_cell() const override {
|
||||
TRY_RESULT(loaded_cell, cell_->load_cell());
|
||||
if (tree_node_.on_load()) {
|
||||
CHECK(loaded_cell.tree_node.empty());
|
||||
loaded_cell.tree_node = tree_node_;
|
||||
}
|
||||
return std::move(loaded_cell);
|
||||
}
|
||||
Ref<Cell> virtualize(VirtualizationParameters virt) const override {
|
||||
auto virtualized_cell = cell_->virtualize(virt);
|
||||
if (tree_node_.empty()) {
|
||||
return virtualized_cell;
|
||||
}
|
||||
if (virtualized_cell.get() == cell_.get()) {
|
||||
return Ref<Cell>(this);
|
||||
}
|
||||
return create(std::move(virtualized_cell), tree_node_);
|
||||
}
|
||||
|
||||
td::uint32 get_virtualization() const override {
|
||||
return cell_->get_virtualization();
|
||||
}
|
||||
|
||||
CellUsageTree::NodePtr get_tree_node() const override {
|
||||
return tree_node_;
|
||||
}
|
||||
|
||||
bool is_loaded() const override {
|
||||
return cell_->is_loaded();
|
||||
}
|
||||
|
||||
// hash and level
|
||||
LevelMask get_level_mask() const override {
|
||||
return cell_->get_level_mask();
|
||||
}
|
||||
|
||||
protected:
|
||||
const Hash do_get_hash(td::uint32 level) const override {
|
||||
return cell_->get_hash(level);
|
||||
}
|
||||
td::uint16 do_get_depth(td::uint32 level) const override {
|
||||
return cell_->get_depth(level);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Cell> cell_;
|
||||
CellUsageTree::NodePtr tree_node_;
|
||||
};
|
||||
} // namespace vm
|
87
crypto/vm/cells/VirtualCell.h
Normal file
87
crypto/vm/cells/VirtualCell.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
|
||||
namespace vm {
|
||||
class VirtualCell : public Cell {
|
||||
private:
|
||||
struct PrivateTag {};
|
||||
|
||||
public:
|
||||
static Ref<Cell> create(VirtualizationParameters virt, Ref<Cell> cell) {
|
||||
if (cell->get_level() <= virt.get_level()) {
|
||||
return cell;
|
||||
}
|
||||
return Ref<VirtualCell>{true, virt, std::move(cell), PrivateTag{}};
|
||||
}
|
||||
|
||||
VirtualCell(VirtualizationParameters virt, Ref<Cell> cell, PrivateTag) : virt_(virt), cell_(std::move(cell)) {
|
||||
CHECK(cell_->get_virtualization() <= virt_.get_virtualization());
|
||||
}
|
||||
|
||||
// load interface
|
||||
td::Result<LoadedCell> load_cell() const override {
|
||||
TRY_RESULT(loaded_cell, cell_->load_cell());
|
||||
loaded_cell.virt = loaded_cell.virt.apply(virt_);
|
||||
return std::move(loaded_cell);
|
||||
}
|
||||
|
||||
Ref<Cell> virtualize(VirtualizationParameters virt) const override {
|
||||
auto new_virt = virt_.apply(virt);
|
||||
if (new_virt == virt_) {
|
||||
return Ref<Cell>(this);
|
||||
}
|
||||
return create(new_virt, cell_);
|
||||
}
|
||||
|
||||
td::uint32 get_virtualization() const override {
|
||||
return virt_.get_virtualization();
|
||||
}
|
||||
|
||||
CellUsageTree::NodePtr get_tree_node() const override {
|
||||
return cell_->get_tree_node();
|
||||
}
|
||||
|
||||
bool is_loaded() const override {
|
||||
return cell_->is_loaded();
|
||||
}
|
||||
|
||||
// hash and level
|
||||
LevelMask get_level_mask() const override {
|
||||
return cell_->get_level_mask().apply(virt_.get_level());
|
||||
}
|
||||
|
||||
protected:
|
||||
const Hash do_get_hash(td::uint32 level) const override {
|
||||
return cell_->get_hash(fix_level(level));
|
||||
}
|
||||
td::uint16 do_get_depth(td::uint32 level) const override {
|
||||
return cell_->get_depth(fix_level(level));
|
||||
}
|
||||
|
||||
private:
|
||||
VirtualizationParameters virt_;
|
||||
Ref<Cell> cell_;
|
||||
|
||||
int fix_level(int level) const {
|
||||
return get_level_mask().apply(level).get_level();
|
||||
}
|
||||
};
|
||||
} // namespace vm
|
72
crypto/vm/cells/VirtualizationParameters.h
Normal file
72
crypto/vm/cells/VirtualizationParameters.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace vm {
|
||||
namespace detail {
|
||||
class VirtualizationParameters {
|
||||
public:
|
||||
static constexpr td::uint8 max_level() {
|
||||
return std::numeric_limits<td::uint8>::max();
|
||||
}
|
||||
|
||||
VirtualizationParameters() = default;
|
||||
|
||||
VirtualizationParameters(td::uint8 level, td::uint8 virtualization) : level_(level), virtualization_(virtualization) {
|
||||
CHECK(virtualization_ != 0 || empty());
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return level_ == max_level() && virtualization_ == 0;
|
||||
}
|
||||
|
||||
VirtualizationParameters apply(VirtualizationParameters outer) const {
|
||||
if (outer.level_ >= level_) {
|
||||
return *this;
|
||||
}
|
||||
CHECK(virtualization_ <= outer.virtualization_);
|
||||
return {outer.level_, outer.virtualization_};
|
||||
}
|
||||
|
||||
td::uint8 get_level() const {
|
||||
return level_;
|
||||
}
|
||||
|
||||
td::uint8 get_virtualization() const {
|
||||
return virtualization_;
|
||||
}
|
||||
|
||||
bool operator==(const VirtualizationParameters &other) const {
|
||||
return level_ == other.level_ && virtualization_ == other.virtualization_;
|
||||
}
|
||||
|
||||
private:
|
||||
td::uint8 level_ = max_level();
|
||||
td::uint8 virtualization_ = 0;
|
||||
};
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const VirtualizationParameters &virt) {
|
||||
return sb << "{level: " << virt.get_level() << ", virtualization: " << virt.get_virtualization() << "}";
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace vm
|
Loading…
Add table
Add a link
Reference in a new issue