mirror of
https://github.com/danog/ton.git
synced 2024-12-02 17:38:33 +01:00
594 lines
17 KiB
C++
594 lines
17 KiB
C++
/*
|
|
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
|