1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

59
crypto/vm/cells/Cell.cpp Normal file
View 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
View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

328
crypto/vm/cells/CellSlice.h Normal file
View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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