1
0
mirror of https://github.com/danog/ton.git synced 2024-11-26 20:14:55 +01:00
ton/crypto/block/block.h
ton fd7a8de970 updated tonlib, block routing
- upated tonlib
- fixed bug in message routing
2019-09-28 11:44:38 +04:00

668 lines
26 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
*/
#pragma once
#include "common/refcnt.hpp"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/dict.h"
#include "vm/boc.h"
#include "vm/stack.hpp"
#include <ostream>
#include "tl/tlblib.hpp"
#include "td/utils/bits.h"
#include "td/utils/StringBuilder.h"
#include "ton/ton-types.h"
namespace block {
using td::Ref;
struct PublicKey {
std::string key;
static td::Result<PublicKey> from_bytes(td::Slice key);
static td::Result<PublicKey> parse(td::Slice key);
std::string serialize(bool base64_url = false);
};
struct StdAddress {
ton::WorkchainId workchain{ton::workchainInvalid};
bool bounceable{true}; // addresses must be bounceable by default
bool testnet{false};
ton::StdSmcAddress addr;
StdAddress() = default;
StdAddress(ton::WorkchainId _wc, const ton::StdSmcAddress& _addr, bool _bounce = true, bool _testnet = false)
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
}
StdAddress(ton::WorkchainId _wc, td::ConstBitPtr _addr, bool _bounce = true, bool _testnet = false)
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
}
explicit StdAddress(std::string serialized);
explicit StdAddress(td::Slice from);
bool is_valid() const {
return workchain != ton::workchainInvalid;
}
bool invalidate() {
workchain = ton::workchainInvalid;
return false;
}
std::string rserialize(bool base64_url = false) const;
bool rserialize_to(td::MutableSlice to, bool base64_url = false) const;
bool rserialize_to(char to[48], bool base64_url = false) const;
bool rdeserialize(td::Slice from);
bool rdeserialize(std::string from);
bool rdeserialize(const char from[48]);
bool parse_addr(td::Slice acc_string);
bool operator==(const StdAddress& other) const;
static td::Result<StdAddress> parse(td::Slice acc_string);
};
inline td::StringBuilder& operator<<(td::StringBuilder& sb, const StdAddress& addr) {
return sb << addr.workchain << " : " << addr.addr.to_hex();
}
bool parse_std_account_addr(td::Slice acc_string, ton::WorkchainId& wc, ton::StdSmcAddress& addr,
bool* bounceable = nullptr, bool* testnet_only = nullptr);
struct ShardId {
ton::WorkchainId workchain_id;
int shard_pfx_len;
unsigned long long shard_pfx;
ShardId(ton::WorkchainId wc_id = ton::workchainInvalid)
: workchain_id(wc_id), shard_pfx_len(0), shard_pfx(1ULL << 63) {
}
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx);
ShardId(ton::ShardIdFull ton_shard);
ShardId(ton::BlockId ton_block);
ShardId(const ton::BlockIdExt& ton_block);
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx, int sh_pfx_len);
ShardId(vm::CellSlice& cs) {
deserialize(cs);
}
ShardId(Ref<vm::CellSlice> cs_ref) {
vm::CellSlice cs{*cs_ref};
deserialize(cs);
}
explicit operator ton::ShardIdFull() const {
return ton::ShardIdFull{workchain_id, shard_pfx};
}
bool operator==(const ShardId& other) const {
return workchain_id == other.workchain_id && shard_pfx == other.shard_pfx;
}
void invalidate() {
workchain_id = ton::workchainInvalid;
shard_pfx_len = 0;
}
bool is_valid() const {
return workchain_id != ton::workchainInvalid;
}
void show(std::ostream& os) const;
std::string to_str() const;
bool serialize(vm::CellBuilder& cb) const;
bool deserialize(vm::CellSlice& cs);
private:
void init();
};
struct EnqueuedMsgDescr {
ton::AccountIdPrefixFull src_prefix_, cur_prefix_, next_prefix_, dest_prefix_;
ton::LogicalTime lt_;
ton::LogicalTime enqueued_lt_;
ton::Bits256 hash_;
Ref<vm::Cell> msg_;
Ref<vm::Cell> msg_env_;
EnqueuedMsgDescr() = default;
EnqueuedMsgDescr(ton::AccountIdPrefixFull cur_pfx, ton::AccountIdPrefixFull next_pfx, ton::LogicalTime lt,
ton::LogicalTime enqueued_lt, td::ConstBitPtr hash)
: cur_prefix_(cur_pfx), next_prefix_(next_pfx), lt_(lt), enqueued_lt_(enqueued_lt), hash_(hash) {
}
bool is_valid() const {
return next_prefix_.is_valid();
}
bool check_key(td::ConstBitPtr key) const;
bool invalidate() {
next_prefix_.workchain = cur_prefix_.workchain = ton::workchainInvalid;
return false;
}
bool unpack(vm::CellSlice& cs);
bool same_workchain() const {
return cur_prefix_.workchain == next_prefix_.workchain;
}
};
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;
struct MsgProcessedUpto {
ton::ShardId shard;
ton::BlockSeqno mc_seqno;
ton::LogicalTime last_inmsg_lt;
ton::Bits256 last_inmsg_hash;
compute_shard_end_lt_func_t compute_shard_end_lt;
MsgProcessedUpto() = default;
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
}
bool operator<(const MsgProcessedUpto& other) const & {
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
}
bool contains(const MsgProcessedUpto& other) const &;
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
ton::BlockSeqno other_mc_seqno) const &;
// NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const;
};
struct MsgProcessedUptoCollection {
ton::ShardIdFull owner;
bool valid{false};
std::vector<MsgProcessedUpto> list;
MsgProcessedUptoCollection(ton::ShardIdFull _owner) : owner(_owner) {
}
MsgProcessedUptoCollection(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
static std::unique_ptr<MsgProcessedUptoCollection> unpack(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
bool is_valid() const {
return valid;
}
bool insert(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash);
bool insert_infty(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt = ~0ULL);
bool compactify();
bool pack(vm::CellBuilder& cb);
bool is_reduced() const;
bool contains(const MsgProcessedUpto& other) const;
bool contains(const MsgProcessedUptoCollection& other) const;
const MsgProcessedUpto* is_simple_update_of(const MsgProcessedUptoCollection& other, bool& ok) const;
ton::BlockSeqno min_mc_seqno() const;
bool split(ton::ShardIdFull new_owner);
bool combine_with(const MsgProcessedUptoCollection& other);
// NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const;
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)>) const;
};
struct ParamLimits {
enum { limits_cnt = 4 };
enum { cl_underload = 0, cl_normal = 1, cl_soft = 2, cl_medium = 3, cl_hard = 4 };
ParamLimits() = default;
ParamLimits(td::uint32 underload, td::uint32 soft_lim, td::uint32 hard_lim)
: limits_{underload, soft_lim, (soft_lim + hard_lim) / 2, hard_lim} {
}
td::uint32 underload() const {
return limits_[0];
}
td::uint32 soft() const {
return limits_[1];
}
td::uint32 hard() const {
return limits_[3];
}
bool compute_medium_limit() {
limits_[2] = soft() + ((hard() - soft()) >> 1);
return true;
}
bool deserialize(vm::CellSlice& cs);
int classify(td::uint64 value) const;
bool fits(unsigned cls, td::uint64 value) const;
private:
std::array<td::uint32, limits_cnt> limits_;
};
struct BlockLimits {
ParamLimits bytes, gas, lt_delta;
ton::LogicalTime start_lt{0};
const vm::CellUsageTree* usage_tree{nullptr};
bool deserialize(vm::CellSlice& cs);
int classify_size(td::uint64 size) const;
int classify_gas(td::uint64 gas) const;
int classify_lt(ton::LogicalTime lt) const;
int classify(td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
bool fits(unsigned cls, td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
};
struct BlockLimitStatus {
const BlockLimits& limits;
ton::LogicalTime cur_lt;
td::uint64 gas_used{};
vm::NewCellStorageStat st_stat;
unsigned accounts{}, transactions{};
BlockLimitStatus(const BlockLimits& limits_, ton::LogicalTime lt = 0)
: limits(limits_), cur_lt(std::max(limits_.start_lt, lt)) {
}
void reset() {
cur_lt = limits.start_lt;
st_stat.set_zero();
transactions = accounts = 0;
gas_used = 0;
}
td::uint64 estimate_block_size(const vm::NewCellStorageStat::Stat* extra = nullptr) const;
int classify() const;
bool fits(unsigned cls) const;
bool would_fit(unsigned cls, ton::LogicalTime end_lt, td::uint64 more_gas,
const vm::NewCellStorageStat::Stat* extra = nullptr) const;
bool add_cell(Ref<vm::Cell> cell) {
st_stat.add_cell(std::move(cell));
return true;
}
bool add_proof(Ref<vm::Cell> cell) {
st_stat.add_proof(std::move(cell), limits.usage_tree);
return true;
}
bool update_lt(ton::LogicalTime lt) {
cur_lt = std::max(lt, cur_lt);
return true;
}
bool update_gas(td::uint64 more_gas) {
gas_used += more_gas;
return true;
}
bool add_transaction(unsigned cnt = 1) {
transactions += cnt;
return true;
}
bool add_account(unsigned cnt = 1) {
accounts += cnt;
return true;
}
};
namespace tlb {
struct CurrencyCollection;
} // namespace tlb
struct CurrencyCollection {
using type_class = block::tlb::CurrencyCollection;
td::RefInt256 grams;
Ref<vm::Cell> extra;
CurrencyCollection() = default;
explicit CurrencyCollection(td::RefInt256 _grams, Ref<vm::Cell> _extra = {})
: grams(std::move(_grams)), extra(std::move(_extra)) {
}
explicit CurrencyCollection(long long _grams, Ref<vm::Cell> _extra = {})
: grams(true, _grams), extra(std::move(_extra)) {
}
bool set_zero() {
grams = td::RefInt256{true, 0};
extra.clear();
return true;
}
static CurrencyCollection zero() {
return CurrencyCollection(td::RefInt256{true, 0});
}
bool is_valid() const {
return grams.not_null();
}
bool is_zero() const {
return is_valid() && extra.is_null() && !td::sgn(grams);
}
bool has_extra() const {
return extra.not_null();
}
bool invalidate() {
extra.clear();
grams.clear();
return false;
}
bool validate() const;
bool validate_extra() const;
bool operator==(const CurrencyCollection& other) const;
bool operator!=(const CurrencyCollection& other) const {
return !operator==(other);
}
bool operator==(td::RefInt256 other_grams) const {
return is_valid() && !has_extra() && !td::cmp(grams, other_grams);
}
bool operator!=(td::RefInt256 other_grams) const {
return !operator==(std::move(other_grams));
}
bool operator>=(const CurrencyCollection& other) const;
bool operator<=(const CurrencyCollection& other) const {
return other >= *this;
}
static bool add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
static bool add(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
CurrencyCollection& operator+=(const CurrencyCollection& other);
CurrencyCollection& operator+=(CurrencyCollection&& other);
CurrencyCollection& operator+=(td::RefInt256 other_grams);
CurrencyCollection operator+(const CurrencyCollection& other) const;
CurrencyCollection operator+(CurrencyCollection&& other) const;
CurrencyCollection operator+(td::RefInt256 other_grams);
static bool sub(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
static bool sub(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
CurrencyCollection& operator-=(const CurrencyCollection& other);
CurrencyCollection& operator-=(CurrencyCollection&& other);
CurrencyCollection& operator-=(td::RefInt256 other_grams);
CurrencyCollection operator-(const CurrencyCollection& other) const;
CurrencyCollection operator-(CurrencyCollection&& other) const;
CurrencyCollection operator-(td::RefInt256 other_grams) const;
bool store(vm::CellBuilder& cb) const;
bool store_or_zero(vm::CellBuilder& cb) const;
bool fetch(vm::CellSlice& cs);
bool fetch_exact(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr);
bool validate_unpack(Ref<vm::CellSlice> csr);
Ref<vm::CellSlice> pack() const;
bool pack_to(Ref<vm::CellSlice>& csr) const {
return (csr = pack()).not_null();
}
Ref<vm::Tuple> as_vm_tuple() const {
if (is_valid()) {
return vm::make_tuple_ref(grams, vm::StackEntry::maybe(extra));
} else {
return {};
}
}
bool show(std::ostream& os) const;
std::string to_str() const;
};
std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc);
struct ShardState {
enum { verbosity = 0 };
ton::BlockIdExt id_;
Ref<vm::Cell> root_;
int global_id_;
ton::UnixTime utime_;
ton::LogicalTime lt_;
ton::BlockSeqno mc_blk_seqno_, min_ref_mc_seqno_;
ton::BlockIdExt mc_blk_ref_;
ton::LogicalTime mc_blk_lt_;
bool before_split_{false};
std::unique_ptr<vm::AugmentedDictionary> account_dict_;
std::unique_ptr<vm::Dictionary> shard_libraries_;
Ref<vm::Cell> mc_state_extra_;
td::uint64 overload_history_{0}, underload_history_{0};
CurrencyCollection total_balance_, total_validator_fees_, global_balance_;
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue_;
std::unique_ptr<vm::Dictionary> ihr_pending_;
std::unique_ptr<vm::Dictionary> block_create_stats_;
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto_;
bool is_valid() const {
return id_.is_valid();
}
bool is_masterchain() const {
return id_.is_masterchain();
}
bool invalidate() {
id_.invalidate();
return false;
}
td::Status unpack_state(ton::BlockIdExt id, Ref<vm::Cell> state_root);
td::Status unpack_state_ext(ton::BlockIdExt id, Ref<vm::Cell> state_root, int global_id,
ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool clear_history,
std::function<bool(ton::BlockSeqno)> for_each_mcseqno);
td::Status merge_with(ShardState& sib);
td::Result<std::unique_ptr<vm::AugmentedDictionary>> compute_split_out_msg_queue(ton::ShardIdFull subshard);
td::Result<std::shared_ptr<block::MsgProcessedUptoCollection>> compute_split_processed_upto(
ton::ShardIdFull subshard);
td::Status split(ton::ShardIdFull subshard);
td::Status unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_info);
bool clear_load_history() {
overload_history_ = underload_history_ = 0;
return true;
}
bool clear_load_history_if(bool cond) {
return !cond || clear_load_history();
}
td::Status check_before_split(bool before_split) const;
td::Status check_global_id(int req_global_id) const;
td::Status check_mc_blk_seqno(ton::BlockSeqno last_mc_block_seqno) const;
bool update_prev_utime_lt(ton::UnixTime& prev_utime, ton::LogicalTime& prev_lt) const;
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)> func) const {
return processed_upto_ && processed_upto_->for_each_mcseqno(std::move(func));
}
};
struct ValueFlow {
struct SetZero {};
CurrencyCollection from_prev_blk, to_next_blk, imported, exported, fees_collected, fees_imported, recovered, created,
minted;
ValueFlow() = default;
ValueFlow(SetZero)
: from_prev_blk{0}
, to_next_blk{0}
, imported{0}
, exported{0}
, fees_collected{0}
, fees_imported{0}
, recovered{0}
, created{0}
, minted{0} {
}
bool is_valid() const {
return from_prev_blk.is_valid() && minted.is_valid();
}
bool validate() const;
bool invalidate() {
return from_prev_blk.invalidate();
}
bool set_zero();
bool store(vm::CellBuilder& cb) const;
bool fetch(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr);
bool show(std::ostream& os) const;
std::string to_str() const;
private:
bool show_one(std::ostream& os, const char* str, const CurrencyCollection& cc) const;
};
std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow);
struct DiscountedCounter {
struct SetZero {};
bool valid;
ton::UnixTime last_updated;
td::uint64 total;
td::uint64 cnt2048;
td::uint64 cnt65536;
DiscountedCounter() : valid(false) {
}
DiscountedCounter(SetZero) : valid(true), last_updated(0), total(0), cnt2048(0), cnt65536(0) {
}
DiscountedCounter(ton::UnixTime _lastupd, td::uint64 _total, td::uint64 _cnt2048, td::uint64 _cnt65536)
: valid(true), last_updated(_lastupd), total(_total), cnt2048(_cnt2048), cnt65536(_cnt65536) {
}
static DiscountedCounter Zero() {
return SetZero();
}
bool is_valid() const {
return valid;
}
bool invalidate() {
return (valid = false);
}
bool set_zero() {
last_updated = 0;
total = cnt2048 = cnt65536 = 0;
return (valid = true);
}
bool is_zero() const {
return !total;
}
bool almost_zero() const {
return (cnt2048 | cnt65536) <= 1;
}
bool operator==(const DiscountedCounter& other) const {
return last_updated == other.last_updated && total == other.total && cnt2048 == other.cnt2048 &&
cnt65536 == other.cnt65536;
}
bool almost_equals(const DiscountedCounter& other) const {
return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 &&
other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1;
}
bool validate();
bool increase_by(unsigned count, ton::UnixTime now);
bool fetch(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr);
bool store(vm::CellBuilder& cb) const;
Ref<vm::CellSlice> pack() const;
bool show(std::ostream& os) const;
std::string to_str() const;
};
static inline std::ostream& operator<<(std::ostream& os, const DiscountedCounter& dcount) {
dcount.show(os);
return os;
}
bool fetch_CreatorStats(vm::CellSlice& cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt);
bool store_CreatorStats(vm::CellBuilder& cb, const DiscountedCounter& mc_cnt, const DiscountedCounter& shard_cnt);
bool unpack_CreatorStats(Ref<vm::CellSlice> cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt);
struct BlockProofLink {
ton::BlockIdExt from, to;
bool is_key{false}, is_fwd{false};
Ref<vm::Cell> dest_proof, state_proof, proof;
ton::CatchainSeqno cc_seqno{0};
td::uint32 validator_set_hash{0};
std::vector<ton::BlockSignature> signatures;
BlockProofLink(ton::BlockIdExt _from, ton::BlockIdExt _to, bool _iskey = false)
: from(_from), to(_to), is_key(_iskey), is_fwd(to.seqno() > from.seqno()) {
}
bool incomplete() const {
return dest_proof.is_null();
}
td::Status validate(td::uint32* save_utime = nullptr) const;
};
struct BlockProofChain {
ton::BlockIdExt from, to;
int mode;
td::uint32 last_utime{0};
bool complete{false}, has_key_block{false}, has_utime{false}, valid{false};
ton::BlockIdExt key_blkid;
std::vector<BlockProofLink> links;
std::size_t link_count() const {
return links.size();
}
BlockProofChain(ton::BlockIdExt _from, ton::BlockIdExt _to, int _mode = 0) : from(_from), to(_to), mode(_mode) {
}
BlockProofLink& new_link(const ton::BlockIdExt& cur, const ton::BlockIdExt& next, bool iskey = false) {
links.emplace_back(cur, next, iskey);
return links.back();
}
const BlockProofLink& last_link() const {
return links.back();
}
BlockProofLink& last_link() {
return links.back();
}
bool last_link_incomplete() const {
return !links.empty() && last_link().incomplete();
}
td::Status validate();
};
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);
bool pack_std_smc_addr_to(char result[48], bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr,
bool bounceable, bool testnet);
std::string pack_std_smc_addr(bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr, bool bounceable,
bool testnet);
bool unpack_std_smc_addr(const char packed[48], ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool unpack_std_smc_addr(td::Slice packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool unpack_std_smc_addr(std::string packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value);
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value1, unsigned long long value2);
bool store_Maybe_Grams(vm::CellBuilder& cb, td::RefInt256 value);
bool store_Maybe_Grams_nz(vm::CellBuilder& cb, td::RefInt256 value);
bool store_CurrencyCollection(vm::CellBuilder& cb, td::RefInt256 value, Ref<vm::Cell> extra);
bool fetch_CurrencyCollection(vm::CellSlice& cs, td::RefInt256& value, Ref<vm::Cell>& extra, bool inexact = false);
bool unpack_CurrencyCollection(Ref<vm::CellSlice> csr, td::RefInt256& value, Ref<vm::Cell>& extra);
bool valid_library_collection(Ref<vm::Cell> cell, bool catch_errors = true);
bool valid_config_data(Ref<vm::Cell> cell, const td::BitArray<256>& addr, bool catch_errors = true,
bool relax_par0 = false);
bool add_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
bool sub_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest,
int used_dest_bits);
bool interpolate_addr_to(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int used_dest_bits,
ton::AccountIdPrefixFull& res);
// result: (transit_addr_dest_bits, nh_addr_dest_bits)
std::pair<int, int> perform_hypercube_routing(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest,
ton::ShardIdFull cur, int used_dest_bits = 0);
bool compute_out_msg_queue_key(Ref<vm::Cell> msg_env, td::BitArray<352>& key);
bool unpack_block_prev_blk(Ref<vm::Cell> block_root, const ton::BlockIdExt& id, std::vector<ton::BlockIdExt>& prev,
ton::BlockIdExt& mc_blkid, bool& after_split, ton::BlockIdExt* fetch_blkid = nullptr);
td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
ton::BlockIdExt* fetch_blkid = nullptr);
td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
ton::BlockIdExt* fetch_blkid = nullptr);
td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
ton::Bits256* store_shard_hash_to = nullptr);
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool get_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool unpack_old_mc_block_id(Ref<vm::CellSlice> old_blk_info, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool check_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, const ton::BlockIdExt& blkid);
bool check_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, const ton::BlockIdExt& blkid);
td::Result<Ref<vm::Cell>> get_block_transaction(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
td::Result<Ref<vm::Cell>> get_block_transaction_try(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
bool get_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell>& in_msg);
bool is_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
bool is_transaction_out_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
bool get_transaction_id(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& account_addr, ton::LogicalTime& lt);
bool get_transaction_owner(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& addr);
td::uint32 compute_validator_set_hash(ton::CatchainSeqno cc_seqno, ton::ShardIdFull from,
const std::vector<ton::ValidatorDescr>& nodes);
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::Cell> acc_root);
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::CellSlice> acc_csr);
bool important_config_parameters_changed(Ref<vm::Cell> old_cfg_root, Ref<vm::Cell> new_cfg_root, bool coarse = false);
bool is_public_library(td::ConstBitPtr key, Ref<vm::CellSlice> val);
bool parse_hex_hash(const char* str, const char* end, td::Bits256& hash);
bool parse_hex_hash(td::Slice str, td::Bits256& hash);
bool parse_block_id_ext(const char* str, const char* end, ton::BlockIdExt& blkid);
bool parse_block_id_ext(td::Slice str, ton::BlockIdExt& blkid);
} // namespace block