1
0
mirror of https://github.com/danog/ton.git synced 2024-12-02 17:38:33 +01:00
ton/crypto/block/block.h
ton 13140ddf29 updated block header
1. Updated block header, proofs now contain more data
   Notice, that old proofs may become invalid in the future
2. Fixed message routing
3. Fixed block creator id in block header
4. Support for full proofs in tonlib
5. Support for partial state download
6. Some other bugfixes
2019-09-18 21:46:32 +04:00

665 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);
};
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