mirror of
https://github.com/danog/ton.git
synced 2024-11-30 04:29:19 +01:00
402 lines
15 KiB
C++
402 lines
15 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 "common/refint.h"
|
|
#include "vm/cells.h"
|
|
#include "vm/cellslice.h"
|
|
#include "vm/dict.h"
|
|
#include "vm/boc.h"
|
|
#include <ostream>
|
|
#include "tl/tlblib.hpp"
|
|
#include "td/utils/bits.h"
|
|
#include "ton/ton-types.h"
|
|
#include "block/block.h"
|
|
#include "block/mc-config.h"
|
|
|
|
namespace block {
|
|
using td::Ref;
|
|
using LtCellRef = std::pair<ton::LogicalTime, Ref<vm::Cell>>;
|
|
|
|
struct Account;
|
|
struct Transaction;
|
|
|
|
struct CollatorError {
|
|
std::string msg;
|
|
CollatorError(std::string _msg) : msg(_msg) {
|
|
}
|
|
CollatorError(const char* _msg) : msg(_msg) {
|
|
}
|
|
std::string get_msg() const {
|
|
return msg;
|
|
}
|
|
};
|
|
|
|
static inline bool operator<(const LtCellRef& a, const LtCellRef& b) {
|
|
return a.first < b.first;
|
|
}
|
|
|
|
struct LtCellCompare {
|
|
bool operator()(const LtCellRef& a, const LtCellRef& b) {
|
|
return a.first < b.first;
|
|
}
|
|
};
|
|
|
|
struct NewOutMsg {
|
|
ton::LogicalTime lt;
|
|
Ref<vm::Cell> msg;
|
|
Ref<vm::Cell> trans;
|
|
NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
|
|
: lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
|
|
}
|
|
bool operator<(const NewOutMsg& other) const & {
|
|
return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
|
|
}
|
|
bool operator>(const NewOutMsg& other) const & {
|
|
return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
|
|
}
|
|
};
|
|
|
|
struct StoragePhaseConfig {
|
|
const std::vector<block::StoragePrices>* pricing{nullptr};
|
|
td::RefInt256 freeze_due_limit;
|
|
td::RefInt256 delete_due_limit;
|
|
StoragePhaseConfig() = default;
|
|
StoragePhaseConfig(const std::vector<block::StoragePrices>* _pricing, td::RefInt256 freeze_limit = {},
|
|
td::RefInt256 delete_limit = {})
|
|
: pricing(_pricing), freeze_due_limit(freeze_limit), delete_due_limit(delete_limit) {
|
|
}
|
|
};
|
|
|
|
struct StoragePhase {
|
|
td::RefInt256 fees_collected;
|
|
td::RefInt256 fees_due;
|
|
ton::UnixTime last_paid_updated;
|
|
bool frozen{false};
|
|
bool deleted{false};
|
|
bool is_special{false};
|
|
};
|
|
|
|
struct ComputePhaseConfig {
|
|
td::uint64 gas_price;
|
|
td::uint64 gas_limit;
|
|
td::uint64 gas_credit;
|
|
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
|
|
td::RefInt256 gas_price256;
|
|
td::RefInt256 max_gas_threshold;
|
|
std::unique_ptr<vm::Dictionary> libraries;
|
|
Ref<vm::Cell> global_config;
|
|
td::BitArray<256> block_rand_seed;
|
|
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
|
|
: gas_price(_gas_price), gas_limit(_gas_limit), gas_credit(_gas_credit) {
|
|
compute_threshold();
|
|
}
|
|
void compute_threshold();
|
|
td::uint64 gas_bought_for(td::RefInt256 nanograms) const;
|
|
td::RefInt256 compute_gas_price(td::uint64 gas_used) const;
|
|
void set_gas_price(td::uint64 _gas_price) {
|
|
gas_price = _gas_price;
|
|
compute_threshold();
|
|
}
|
|
Ref<vm::Cell> lookup_library(td::ConstBitPtr key) const;
|
|
Ref<vm::Cell> lookup_library(const td::Bits256& key) const {
|
|
return lookup_library(key.bits());
|
|
}
|
|
Ref<vm::Cell> get_lib_root() const {
|
|
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
|
|
}
|
|
};
|
|
|
|
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
|
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
|
|
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
|
|
|
struct MsgPrices {
|
|
td::uint64 lump_price;
|
|
td::uint64 bit_price;
|
|
td::uint64 cell_price;
|
|
td::uint32 ihr_factor;
|
|
td::uint32 first_frac;
|
|
td::uint32 next_frac;
|
|
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
|
|
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
|
|
bool ihr_disabled = false) const;
|
|
MsgPrices() = default;
|
|
MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf)
|
|
: lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) {
|
|
}
|
|
td::RefInt256 get_first_part(td::RefInt256 total) const;
|
|
td::uint64 get_first_part(td::uint64 total) const;
|
|
td::RefInt256 get_next_part(td::RefInt256 total) const;
|
|
};
|
|
|
|
struct ActionPhaseConfig {
|
|
int max_actions{255};
|
|
MsgPrices fwd_std;
|
|
MsgPrices fwd_mc; // from/to masterchain
|
|
const WorkchainSet* workchains{nullptr};
|
|
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
|
|
return is_masterchain ? fwd_mc : fwd_std;
|
|
}
|
|
};
|
|
|
|
struct CreditPhase {
|
|
td::RefInt256 due_fees_collected;
|
|
block::CurrencyCollection credit;
|
|
// td::RefInt256 credit;
|
|
// Ref<vm::Cell> credit_extra;
|
|
};
|
|
|
|
struct ComputePhase {
|
|
enum { sk_none, sk_no_state, sk_bad_state, sk_no_gas };
|
|
int skip_reason{sk_none};
|
|
bool success{false};
|
|
bool msg_state_used{false};
|
|
bool account_activated{false};
|
|
bool out_of_gas{false};
|
|
bool accepted{false};
|
|
td::RefInt256 gas_fees;
|
|
td::uint64 gas_used, gas_max, gas_limit, gas_credit;
|
|
int mode;
|
|
int exit_code;
|
|
int exit_arg;
|
|
int vm_steps;
|
|
ton::Bits256 vm_init_state_hash, vm_final_state_hash;
|
|
Ref<vm::Cell> in_msg;
|
|
Ref<vm::Cell> new_data;
|
|
Ref<vm::Cell> actions;
|
|
};
|
|
|
|
struct ActionPhase {
|
|
bool success{false};
|
|
bool valid{false};
|
|
bool no_funds{false};
|
|
bool code_changed{false};
|
|
bool action_list_invalid{false};
|
|
bool acc_delete_req{false};
|
|
enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 };
|
|
int acc_status_change{acst_unchanged};
|
|
td::RefInt256 total_fwd_fees; // all fees debited from the account
|
|
td::RefInt256 total_action_fees; // fees credited to validators in this action phase
|
|
int result_code;
|
|
int result_arg;
|
|
int tot_actions;
|
|
int spec_actions;
|
|
int skipped_actions;
|
|
int msgs_created;
|
|
Ref<vm::Cell> new_code;
|
|
td::BitArray<256> action_list_hash;
|
|
block::CurrencyCollection remaining_balance, reserved_balance;
|
|
// td::RefInt256 remaining_balance;
|
|
// Ref<vm::Cell> remaining_extra;
|
|
// td::RefInt256 reserved_balance;
|
|
// Ref<vm::Cell> reserved_extra;
|
|
std::vector<Ref<vm::Cell>> action_list; // processed in reverse order
|
|
std::vector<Ref<vm::Cell>> out_msgs;
|
|
ton::LogicalTime end_lt;
|
|
unsigned long long tot_msg_bits{0}, tot_msg_cells{0};
|
|
};
|
|
|
|
struct BouncePhase {
|
|
bool ok{false};
|
|
bool nofunds{false};
|
|
unsigned long long msg_bits, msg_cells;
|
|
unsigned long long fwd_fees, fwd_fees_collected;
|
|
Ref<vm::Cell> out_msg;
|
|
};
|
|
|
|
struct Account {
|
|
enum { acc_nonexist = 0, acc_uninit = 1, acc_frozen = 2, acc_active = 3, acc_deleted = 4 };
|
|
int status{acc_nonexist}, orig_status{acc_nonexist};
|
|
bool is_special{false};
|
|
bool tick{false};
|
|
bool tock{false};
|
|
bool created{false};
|
|
bool split_depth_set_{false};
|
|
unsigned char split_depth_{0};
|
|
int verbosity{3 * 0};
|
|
ton::UnixTime now_{0};
|
|
ton::WorkchainId workchain{ton::workchainInvalid};
|
|
td::BitArray<32> addr_rewrite; // rewrite (anycast) data, split_depth bits
|
|
ton::StdSmcAddress addr; // rewritten address (by replacing a prefix of `addr_orig` with `addr_rewrite`)
|
|
ton::StdSmcAddress addr_orig; // address indicated in smart-contract data
|
|
Ref<vm::CellSlice> my_addr; // address as stored in the smart contract (MsgAddressInt)
|
|
Ref<vm::CellSlice> my_addr_exact; // exact address without anycast info
|
|
ton::LogicalTime last_trans_end_lt_;
|
|
ton::LogicalTime last_trans_lt_;
|
|
ton::Bits256 last_trans_hash_;
|
|
ton::LogicalTime block_lt;
|
|
ton::UnixTime last_paid;
|
|
vm::CellStorageStat storage_stat;
|
|
block::CurrencyCollection balance;
|
|
// td::RefInt256 balance;
|
|
// Ref<vm::Cell> extra_balance;
|
|
td::RefInt256 due_payment;
|
|
Ref<vm::Cell> orig_total_state; // ^Account
|
|
Ref<vm::Cell> total_state; // ^Account
|
|
Ref<vm::CellSlice> inner_state; // StateInit
|
|
ton::Bits256 state_hash; // hash of StateInit for frozen accounts
|
|
Ref<vm::Cell> code, data, library, orig_library;
|
|
std::vector<LtCellRef> transactions;
|
|
Account() = default;
|
|
Account(ton::WorkchainId wc, td::ConstBitPtr _addr) : workchain(wc), addr(_addr) {
|
|
}
|
|
Account(ton::WorkchainId wc, td::ConstBitPtr _addr, int depth)
|
|
: split_depth_set_(true), split_depth_((unsigned char)depth), workchain(wc), addr(_addr) {
|
|
}
|
|
block::CurrencyCollection get_balance() const {
|
|
return balance;
|
|
}
|
|
bool set_address(ton::WorkchainId wc, td::ConstBitPtr new_addr);
|
|
bool unpack(Ref<vm::CellSlice> account, Ref<vm::CellSlice> extra, ton::UnixTime now, bool special = false);
|
|
bool init_new(ton::UnixTime now);
|
|
bool recompute_tmp_addr(Ref<vm::CellSlice>& tmp_addr, int split_depth, td::ConstBitPtr orig_addr_rewrite) const;
|
|
td::RefInt256 compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing) const;
|
|
bool is_masterchain() const {
|
|
return workchain == ton::masterchainId;
|
|
}
|
|
bool belongs_to_shard(ton::ShardIdFull shard) const;
|
|
bool store_acc_status(vm::CellBuilder& cb, int status) const;
|
|
bool store_acc_status(vm::CellBuilder& cb) const {
|
|
return store_acc_status(cb, status);
|
|
}
|
|
void push_transaction(Ref<vm::Cell> trans_root, ton::LogicalTime trans_lt);
|
|
bool libraries_changed() const;
|
|
bool create_account_block(vm::CellBuilder& cb); // stores an AccountBlock with all transactions
|
|
|
|
protected:
|
|
friend struct Transaction;
|
|
bool set_split_depth(int split_depth);
|
|
bool check_split_depth(int split_depth) const;
|
|
bool init_rewrite_addr(int split_depth, td::ConstBitPtr orig_addr_rewrite);
|
|
|
|
private:
|
|
bool unpack_address(vm::CellSlice& addr_cs);
|
|
bool unpack_storage_info(vm::CellSlice& cs);
|
|
bool unpack_state(vm::CellSlice& cs);
|
|
bool parse_maybe_anycast(vm::CellSlice& cs);
|
|
bool store_maybe_anycast(vm::CellBuilder& cb) const;
|
|
bool compute_my_addr(bool force = false);
|
|
};
|
|
|
|
struct Transaction {
|
|
static constexpr unsigned max_msg_bits = (1 << 21), max_msg_cells = (1 << 13);
|
|
enum {
|
|
tr_none,
|
|
tr_ord,
|
|
tr_storage,
|
|
tr_tick,
|
|
tr_tock,
|
|
tr_split_prepare,
|
|
tr_split_install,
|
|
tr_merge_prepare,
|
|
tr_merge_install
|
|
};
|
|
int trans_type{tr_none};
|
|
bool was_deleted{false};
|
|
bool was_frozen{false};
|
|
bool was_activated{false};
|
|
bool was_created{false};
|
|
bool bounce_enabled{false};
|
|
bool in_msg_extern{false};
|
|
bool use_msg_state{false};
|
|
bool is_first{false};
|
|
bool orig_addr_rewrite_set{false};
|
|
bool new_tick;
|
|
bool new_tock;
|
|
signed char new_split_depth{-1};
|
|
ton::UnixTime now;
|
|
int acc_status;
|
|
int verbosity{3 * 0};
|
|
int in_msg_type{0};
|
|
const Account& account; // only `commit` method modifies the account
|
|
Ref<vm::CellSlice> my_addr, my_addr_exact; // almost the same as in account.*
|
|
ton::LogicalTime start_lt, end_lt;
|
|
block::CurrencyCollection balance;
|
|
block::CurrencyCollection msg_balance_remaining;
|
|
td::RefInt256 due_payment;
|
|
td::RefInt256 in_fwd_fee, msg_fwd_fees;
|
|
block::CurrencyCollection total_fees{0};
|
|
ton::UnixTime last_paid;
|
|
Ref<vm::Cell> root;
|
|
Ref<vm::Cell> new_total_state;
|
|
Ref<vm::CellSlice> new_inner_state;
|
|
// Ref<vm::Cell> extra_balance;
|
|
// Ref<vm::Cell> msg_extra;
|
|
Ref<vm::Cell> new_code, new_data, new_library;
|
|
Ref<vm::Cell> in_msg, in_msg_state;
|
|
Ref<vm::CellSlice> in_msg_body;
|
|
Ref<vm::Cell> in_msg_library;
|
|
td::BitArray<256> frozen_hash;
|
|
td::BitArray<32> orig_addr_rewrite;
|
|
std::vector<Ref<vm::Cell>> out_msgs;
|
|
std::unique_ptr<StoragePhase> storage_phase;
|
|
std::unique_ptr<CreditPhase> credit_phase;
|
|
std::unique_ptr<ComputePhase> compute_phase;
|
|
std::unique_ptr<ActionPhase> action_phase;
|
|
std::unique_ptr<BouncePhase> bounce_phase;
|
|
vm::CellStorageStat new_storage_stat;
|
|
Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
|
|
Ref<vm::Cell> _inmsg = {});
|
|
bool unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg);
|
|
bool check_in_msg_state_hash();
|
|
bool prepare_storage_phase(const StoragePhaseConfig& cfg, bool force_collect = true);
|
|
bool prepare_credit_phase();
|
|
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
|
|
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
|
|
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
|
|
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
|
|
bool prepare_action_phase(const ActionPhaseConfig& cfg);
|
|
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
|
|
bool compute_state();
|
|
bool serialize();
|
|
td::uint64 gas_used() const {
|
|
return compute_phase ? compute_phase->gas_used : 0;
|
|
}
|
|
|
|
td::Result<vm::NewCellStorageStat::Stat> estimate_block_storage_profile_incr(
|
|
const vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
|
|
bool update_block_storage_profile(vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
|
|
bool would_fit(unsigned cls, const block::BlockLimitStatus& blk_lim_st) const;
|
|
bool update_limits(block::BlockLimitStatus& blk_lim_st) const;
|
|
|
|
Ref<vm::Cell> commit(Account& _account); // _account should point to the same account
|
|
LtCellRef extract_out_msg(unsigned i);
|
|
NewOutMsg extract_out_msg_ext(unsigned i);
|
|
void extract_out_msgs(std::vector<LtCellRef>& list);
|
|
|
|
private:
|
|
Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
|
|
bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
|
|
int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
|
int try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
|
int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
|
bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
|
|
bool check_rewrite_dest_addr(Ref<vm::CellSlice>& dest_addr, const ActionPhaseConfig& cfg,
|
|
bool* is_mc = nullptr) const;
|
|
bool serialize_storage_phase(vm::CellBuilder& cb);
|
|
bool serialize_credit_phase(vm::CellBuilder& cb);
|
|
bool serialize_compute_phase(vm::CellBuilder& cb);
|
|
bool serialize_action_phase(vm::CellBuilder& cb);
|
|
bool serialize_bounce_phase(vm::CellBuilder& cb);
|
|
bool unpack_msg_state(bool lib_only = false);
|
|
};
|
|
|
|
} // namespace block
|