mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge branch 'testnet' into block-generation
This commit is contained in:
commit
f4fd3ff3be
246 changed files with 7895 additions and 5430 deletions
|
|
@ -285,8 +285,11 @@ int exec_divmod(VmState* st, unsigned args, int quiet) {
|
|||
typename td::BigInt256::DoubleInt tmp{*x}, quot;
|
||||
tmp += *w;
|
||||
tmp.mod_div(*y, quot, round_mode);
|
||||
stack.push_int_quiet(td::make_refint(quot), quiet);
|
||||
stack.push_int_quiet(td::make_refint(tmp), quiet);
|
||||
auto q = td::make_refint(quot), r = td::make_refint(tmp);
|
||||
q.write().normalize();
|
||||
r.write().normalize();
|
||||
stack.push_int_quiet(std::move(q), quiet);
|
||||
stack.push_int_quiet(std::move(r), quiet);
|
||||
} else {
|
||||
switch (d) {
|
||||
case 1:
|
||||
|
|
@ -399,6 +402,7 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
|
|||
if (mode & 1) {
|
||||
os << 'Q';
|
||||
}
|
||||
std::string end;
|
||||
switch (args & 12) {
|
||||
case 4:
|
||||
os << "RSHIFT";
|
||||
|
|
@ -407,17 +411,22 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
|
|||
os << "MODPOW2";
|
||||
break;
|
||||
case 12:
|
||||
os << "RSHIFTMOD";
|
||||
os << "RSHIFT";
|
||||
end = "MOD";
|
||||
break;
|
||||
case 0:
|
||||
os << "ADDRSHIFTMOD";
|
||||
os << "ADDRSHIFT";
|
||||
end = "MOD";
|
||||
break;
|
||||
}
|
||||
if (!(mode & 2)) {
|
||||
os << end;
|
||||
}
|
||||
if (round_mode) {
|
||||
os << "FRC"[round_mode];
|
||||
}
|
||||
if (mode & 2) {
|
||||
os << ' ' << y;
|
||||
os << "#" << end << ' ' << y;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
|
@ -519,7 +528,7 @@ int exec_mulshrmod(VmState* st, unsigned args, int mode) {
|
|||
if (add) {
|
||||
tmp = *w;
|
||||
}
|
||||
tmp.add_mul(*x, *y);
|
||||
tmp.add_mul(*x, *y).normalize();
|
||||
switch (d) {
|
||||
case 1:
|
||||
tmp.rshift(z, round_mode).normalize();
|
||||
|
|
@ -553,6 +562,7 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
|
|||
if (mode & 1) {
|
||||
os << 'Q';
|
||||
}
|
||||
std::string end;
|
||||
switch (args & 12) {
|
||||
case 4:
|
||||
os << "MULRSHIFT";
|
||||
|
|
@ -561,15 +571,21 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
|
|||
os << "MULMODPOW2";
|
||||
break;
|
||||
case 12:
|
||||
os << "MULRSHIFTMOD";
|
||||
os << "MULRSHIFT";
|
||||
end = "MOD";
|
||||
break;
|
||||
case 0:
|
||||
os << "MULADDRSHIFTMOD";
|
||||
os << "MULADDRSHIFT";
|
||||
end = "MOD";
|
||||
break;
|
||||
}
|
||||
if (round_mode) {
|
||||
os << "FRC"[round_mode];
|
||||
}
|
||||
if (mode & 2) {
|
||||
os << "#";
|
||||
}
|
||||
os << end;
|
||||
if (mode & 2) {
|
||||
os << ' ' << y;
|
||||
}
|
||||
|
|
@ -644,18 +660,22 @@ std::string dump_shldivmod(CellSlice&, unsigned args, int mode) {
|
|||
if (mode & 1) {
|
||||
os << "Q";
|
||||
}
|
||||
os << "LSHIFT";
|
||||
if (mode & 2) {
|
||||
os << "#";
|
||||
}
|
||||
switch (args & 12) {
|
||||
case 4:
|
||||
os << "LSHIFTDIV";
|
||||
os << "DIV";
|
||||
break;
|
||||
case 8:
|
||||
os << "LSHIFTMOD";
|
||||
os << "MOD";
|
||||
break;
|
||||
case 12:
|
||||
os << "LSHIFTDIVMOD";
|
||||
os << "DIVMOD";
|
||||
break;
|
||||
case 0:
|
||||
os << "LSHIFTADDDIVMOD";
|
||||
os << "ADDDIVMOD";
|
||||
break;
|
||||
}
|
||||
if (round_mode) {
|
||||
|
|
|
|||
|
|
@ -930,7 +930,7 @@ unsigned long long BagOfCells::get_idx_entry_raw(int index) {
|
|||
*
|
||||
*/
|
||||
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty, bool allow_nonzero_level) {
|
||||
if (data.empty() && can_be_empty) {
|
||||
return Ref<Cell>();
|
||||
}
|
||||
|
|
@ -946,7 +946,7 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
|
|||
if (root.is_null()) {
|
||||
return td::Status::Error("bag of cells has null root cell (?)");
|
||||
}
|
||||
if (root->get_level() != 0) {
|
||||
if (!allow_nonzero_level && root->get_level() != 0) {
|
||||
return td::Status::Error("bag of cells has a root with non-zero level");
|
||||
}
|
||||
return std::move(root);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class NewCellStorageStat {
|
|||
bool operator==(const Stat& other) const {
|
||||
return key() == other.key();
|
||||
}
|
||||
Stat(const Stat& other) = default;
|
||||
Stat& operator=(const Stat& other) = default;
|
||||
Stat& operator+=(const Stat& other) {
|
||||
cells += other.cells;
|
||||
|
|
@ -336,7 +337,7 @@ class BagOfCells {
|
|||
std::vector<td::uint8>* cell_should_cache);
|
||||
};
|
||||
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false, bool allow_nonzero_level = false);
|
||||
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
|
||||
|
||||
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data,
|
||||
|
|
|
|||
|
|
@ -892,6 +892,40 @@ int exec_load_special_cell(VmState* st, bool quiet) {
|
|||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute XLOAD" << (quiet ? "Q" : "");
|
||||
auto cell = stack.pop_cell();
|
||||
if (st->get_global_version() >= 5) {
|
||||
st->register_cell_load(cell->get_hash());
|
||||
auto r_loaded_cell = cell->load_cell();
|
||||
if (r_loaded_cell.is_error()) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
return 0;
|
||||
} else {
|
||||
throw VmError{Excno::cell_und, "failed to load cell"};
|
||||
}
|
||||
}
|
||||
auto loaded_cell = r_loaded_cell.move_as_ok();
|
||||
if (loaded_cell.data_cell->is_special()) {
|
||||
if (loaded_cell.data_cell->special_type() != CellTraits::SpecialType::Library) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
return 0;
|
||||
} else {
|
||||
throw VmError{Excno::cell_und, "unexpected special cell"};
|
||||
}
|
||||
}
|
||||
CellSlice cs(std::move(loaded_cell));
|
||||
DCHECK(cs.size() == Cell::hash_bits + 8);
|
||||
cell = st->load_library(cs.data_bits() + 8);
|
||||
if (cell.is_null()) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
return 0;
|
||||
} else {
|
||||
throw VmError{Excno::cell_und, "failed to load library cell"};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stack.push_cell(cell);
|
||||
if (quiet) {
|
||||
stack.push_bool(true);
|
||||
|
|
@ -1357,6 +1391,55 @@ int exec_slice_depth(VmState* st) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int exec_cell_level(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute CLEVEL";
|
||||
auto cell = stack.pop_cell();
|
||||
stack.push_smallint(cell->get_level());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_cell_level_mask(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute CLEVELMASK";
|
||||
auto cell = stack.pop_cell();
|
||||
stack.push_smallint(cell->get_level_mask().get_mask());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_cell_hash_i(VmState* st, unsigned args, bool var) {
|
||||
unsigned i;
|
||||
Stack& stack = st->get_stack();
|
||||
if (var) {
|
||||
VM_LOG(st) << "execute CHASHIX";
|
||||
i = stack.pop_smallint_range(3);
|
||||
} else {
|
||||
i = args & 3;
|
||||
VM_LOG(st) << "execute CHASHI " << i;
|
||||
}
|
||||
auto cell = stack.pop_cell();
|
||||
std::array<unsigned char, 32> hash = cell->get_hash(i).as_array();
|
||||
td::RefInt256 res{true};
|
||||
CHECK(res.write().import_bytes(hash.data(), hash.size(), false));
|
||||
stack.push_int(std::move(res));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_cell_depth_i(VmState* st, unsigned args, bool var) {
|
||||
unsigned i;
|
||||
Stack& stack = st->get_stack();
|
||||
if (var) {
|
||||
VM_LOG(st) << "execute CDEPTHIX";
|
||||
i = stack.pop_smallint_range(3);
|
||||
} else {
|
||||
i = args & 3;
|
||||
VM_LOG(st) << "execute CDEPTHI " << i;
|
||||
}
|
||||
auto cell = stack.pop_cell();
|
||||
stack.push_smallint(cell->get_depth(i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cell_deserialize_ops(OpcodeTable& cp0) {
|
||||
using namespace std::placeholders;
|
||||
cp0.insert(OpcodeInstr::mksimple(0xd0, 8, "CTOS", exec_cell_to_slice))
|
||||
|
|
@ -1445,7 +1528,13 @@ void register_cell_deserialize_ops(OpcodeTable& cp0) {
|
|||
.insert(OpcodeInstr::mksimple(0xd761, 16, "LDONES", std::bind(exec_load_same, _1, "LDONES", 1)))
|
||||
.insert(OpcodeInstr::mksimple(0xd762, 16, "LDSAME", std::bind(exec_load_same, _1, "LDSAME", -1)))
|
||||
.insert(OpcodeInstr::mksimple(0xd764, 16, "SDEPTH", exec_slice_depth))
|
||||
.insert(OpcodeInstr::mksimple(0xd765, 16, "CDEPTH", exec_cell_depth));
|
||||
.insert(OpcodeInstr::mksimple(0xd765, 16, "CDEPTH", exec_cell_depth))
|
||||
.insert(OpcodeInstr::mksimple(0xd766, 16, "CLEVEL", exec_cell_level)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xd767, 16, "CLEVELMASK", exec_cell_level_mask)->require_version(6))
|
||||
.insert(OpcodeInstr::mkfixed(0xd768 >> 2, 14, 2, instr::dump_1c_and(3, "CHASHI "), std::bind(exec_cell_hash_i, _1, _2, false))->require_version(6))
|
||||
.insert(OpcodeInstr::mkfixed(0xd76c >> 2, 14, 2, instr::dump_1c_and(3, "CDEPTHI "), std::bind(exec_cell_depth_i, _1, _2, false))->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xd770, 16, "CHASHIX ", std::bind(exec_cell_hash_i, _1, 0, true))->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xd771, 16, "CDEPTHIX ", std::bind(exec_cell_depth_i, _1, 0, true))->require_version(6));
|
||||
}
|
||||
|
||||
void register_cell_ops(OpcodeTable& cp0) {
|
||||
|
|
|
|||
|
|
@ -1056,9 +1056,10 @@ std::ostream& operator<<(std::ostream& os, Ref<CellSlice> 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
|
||||
VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_special) {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
bool library_loaded = false;
|
||||
while (true) {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
if (vm_state_interface) {
|
||||
if (vm_state_interface && !library_loaded) {
|
||||
vm_state_interface->register_cell_load(cell->get_hash());
|
||||
}
|
||||
auto r_loaded_cell = cell->load_cell();
|
||||
|
|
@ -1077,6 +1078,12 @@ VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_specia
|
|||
} else if (loaded_cell.data_cell->is_special()) {
|
||||
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
|
||||
if (vm_state_interface) {
|
||||
if (vm_state_interface->get_global_version() >= 5) {
|
||||
if (library_loaded) {
|
||||
throw VmError{Excno::cell_und, "failed to load library cell: recursive library cells are not allowed"};
|
||||
}
|
||||
library_loaded = true;
|
||||
}
|
||||
CellSlice cs(std::move(loaded_cell));
|
||||
DCHECK(cs.size() == Cell::hash_bits + 8);
|
||||
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
||||
|
|
|
|||
|
|
@ -27,16 +27,25 @@ namespace vm {
|
|||
namespace {
|
||||
class RefcntCellStorer {
|
||||
public:
|
||||
RefcntCellStorer(td::int32 refcnt, const DataCell &cell) : refcnt_(refcnt), cell_(cell) {
|
||||
RefcntCellStorer(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc)
|
||||
: refcnt_(refcnt), cell_(cell), as_boc_(as_boc) {
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
using td::store;
|
||||
if (as_boc_) {
|
||||
td::int32 tag = -1;
|
||||
store(tag, storer);
|
||||
store(refcnt_, storer);
|
||||
td::BufferSlice data = vm::std_boc_serialize(cell_).move_as_ok();
|
||||
storer.store_slice(data);
|
||||
return;
|
||||
}
|
||||
store(refcnt_, storer);
|
||||
store(cell_, storer);
|
||||
for (unsigned i = 0; i < cell_.size_refs(); i++) {
|
||||
auto cell = cell_.get_ref(i);
|
||||
store(*cell_, storer);
|
||||
for (unsigned i = 0; i < cell_->size_refs(); i++) {
|
||||
auto cell = cell_->get_ref(i);
|
||||
auto level_mask = cell->get_level_mask();
|
||||
auto level = level_mask.get_level();
|
||||
td::uint8 x = static_cast<td::uint8>(level_mask.get_mask());
|
||||
|
|
@ -60,7 +69,8 @@ class RefcntCellStorer {
|
|||
|
||||
private:
|
||||
td::int32 refcnt_;
|
||||
const DataCell &cell_;
|
||||
td::Ref<DataCell> cell_;
|
||||
bool as_boc_;
|
||||
};
|
||||
|
||||
class RefcntCellParser {
|
||||
|
|
@ -69,11 +79,17 @@ class RefcntCellParser {
|
|||
}
|
||||
td::int32 refcnt;
|
||||
Ref<DataCell> cell;
|
||||
bool stored_boc_;
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser, ExtCellCreator &ext_cell_creator) {
|
||||
using ::td::parse;
|
||||
parse(refcnt, parser);
|
||||
stored_boc_ = false;
|
||||
if (refcnt == -1) {
|
||||
stored_boc_ = true;
|
||||
parse(refcnt, parser);
|
||||
}
|
||||
if (!need_data_) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -81,6 +97,12 @@ class RefcntCellParser {
|
|||
TRY_STATUS(parser.get_status());
|
||||
auto size = parser.get_left_len();
|
||||
td::Slice data = parser.template fetch_string_raw<td::Slice>(size);
|
||||
if (stored_boc_) {
|
||||
TRY_RESULT(boc, vm::std_boc_deserialize(data, false, true));
|
||||
TRY_RESULT(loaded_cell, boc->load_cell());
|
||||
cell = std::move(loaded_cell.data_cell);
|
||||
return td::Status::OK();
|
||||
}
|
||||
CellSerializationInfo info;
|
||||
auto cell_data = data;
|
||||
TRY_STATUS(info.init(cell_data, 0 /*ref_byte_size*/));
|
||||
|
|
@ -122,7 +144,8 @@ class RefcntCellParser {
|
|||
};
|
||||
} // namespace
|
||||
|
||||
CellLoader::CellLoader(std::shared_ptr<KeyValueReader> reader) : reader_(std::move(reader)) {
|
||||
CellLoader::CellLoader(std::shared_ptr<KeyValueReader> reader, std::function<void(const LoadResult &)> on_load_callback)
|
||||
: reader_(std::move(reader)), on_load_callback_(std::move(on_load_callback)) {
|
||||
CHECK(reader_);
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +168,11 @@ td::Result<CellLoader::LoadResult> CellLoader::load(td::Slice hash, bool need_da
|
|||
|
||||
res.refcnt_ = refcnt_cell.refcnt;
|
||||
res.cell_ = std::move(refcnt_cell.cell);
|
||||
res.stored_boc_ = refcnt_cell.stored_boc_;
|
||||
//CHECK(res.cell_->get_hash() == hash);
|
||||
if (on_load_callback_) {
|
||||
on_load_callback_(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -157,7 +184,7 @@ td::Status CellStorer::erase(td::Slice hash) {
|
|||
return kv_.erase(hash);
|
||||
}
|
||||
|
||||
td::Status CellStorer::set(td::int32 refcnt, const DataCell &cell) {
|
||||
return kv_.set(cell.get_hash().as_slice(), td::serialize(RefcntCellStorer(refcnt, cell)));
|
||||
td::Status CellStorer::set(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc) {
|
||||
return kv_.set(cell->get_hash().as_slice(), td::serialize(RefcntCellStorer(refcnt, cell, as_boc)));
|
||||
}
|
||||
} // namespace vm
|
||||
|
|
|
|||
|
|
@ -45,19 +45,21 @@ class CellLoader {
|
|||
|
||||
Ref<DataCell> cell_;
|
||||
td::int32 refcnt_{0};
|
||||
bool stored_boc_{false};
|
||||
};
|
||||
CellLoader(std::shared_ptr<KeyValueReader> reader);
|
||||
CellLoader(std::shared_ptr<KeyValueReader> reader, std::function<void(const LoadResult &)> on_load_callback = {});
|
||||
td::Result<LoadResult> load(td::Slice hash, bool need_data, ExtCellCreator &ext_cell_creator);
|
||||
|
||||
private:
|
||||
std::shared_ptr<KeyValueReader> reader_;
|
||||
std::function<void(const LoadResult &)> on_load_callback_;
|
||||
};
|
||||
|
||||
class CellStorer {
|
||||
public:
|
||||
CellStorer(KeyValue &kv);
|
||||
td::Status erase(td::Slice hash);
|
||||
td::Status set(td::int32 refcnt, const DataCell &cell);
|
||||
td::Status set(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc);
|
||||
|
||||
private:
|
||||
KeyValue &kv_;
|
||||
|
|
|
|||
|
|
@ -210,6 +210,14 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
void set_celldb_compress_depth(td::uint32 value) override {
|
||||
celldb_compress_depth_ = value;
|
||||
}
|
||||
|
||||
vm::ExtCellCreator& as_ext_cell_creator() override {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<CellLoader> loader_;
|
||||
std::vector<Ref<Cell>> to_inc_;
|
||||
|
|
@ -217,6 +225,7 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
|
|||
CellHashTable<CellInfo> hash_table_;
|
||||
std::vector<CellInfo *> visited_;
|
||||
Stats stats_diff_;
|
||||
td::uint32 celldb_compress_depth_{0};
|
||||
|
||||
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() {
|
||||
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("DynamicBagOfCellsDb");
|
||||
|
|
@ -443,7 +452,8 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
|
|||
guard.dismiss();
|
||||
} else {
|
||||
auto loaded_cell = info.cell->load_cell().move_as_ok();
|
||||
storer.set(info.db_refcnt, *loaded_cell.data_cell);
|
||||
storer.set(info.db_refcnt, loaded_cell.data_cell,
|
||||
loaded_cell.data_cell->get_depth() == celldb_compress_depth_ && celldb_compress_depth_ != 0);
|
||||
info.in_db = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ class DynamicBagOfCellsDb {
|
|||
// restart with new loader will also reset stats_diff
|
||||
virtual td::Status set_loader(std::unique_ptr<CellLoader> loader) = 0;
|
||||
|
||||
virtual void set_celldb_compress_depth(td::uint32 value) = 0;
|
||||
virtual vm::ExtCellCreator& as_ext_cell_creator() = 0;
|
||||
|
||||
static std::unique_ptr<DynamicBagOfCellsDb> create();
|
||||
|
||||
class AsyncExecutor {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "vm/cellslice.h"
|
||||
#include "vm/stack.hpp"
|
||||
#include "common/bitstring.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
#include "td/utils/bits.h"
|
||||
|
||||
|
|
@ -2007,7 +2008,7 @@ bool DictionaryFixed::combine_with(DictionaryFixed& dict2) {
|
|||
|
||||
bool DictionaryFixed::dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer, int n, int total_key_len,
|
||||
const DictionaryFixed::foreach_func_t& foreach_func,
|
||||
bool invert_first) const {
|
||||
bool invert_first, bool shuffle) const {
|
||||
if (dict.is_null()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2026,26 +2027,29 @@ bool DictionaryFixed::dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer,
|
|||
key_buffer += l + 1;
|
||||
if (l) {
|
||||
invert_first = false;
|
||||
} else if (invert_first) {
|
||||
}
|
||||
bool invert = shuffle ? td::Random::fast(0, 1) == 1: invert_first;
|
||||
if (invert) {
|
||||
std::swap(c1, c2);
|
||||
}
|
||||
key_buffer[-1] = invert_first;
|
||||
key_buffer[-1] = invert;
|
||||
// recursive check_foreach applied to both children
|
||||
if (!dict_check_for_each(std::move(c1), key_buffer, n - l - 1, total_key_len, foreach_func)) {
|
||||
if (!dict_check_for_each(std::move(c1), key_buffer, n - l - 1, total_key_len, foreach_func, false, shuffle)) {
|
||||
return false;
|
||||
}
|
||||
key_buffer[-1] = !invert_first;
|
||||
return dict_check_for_each(std::move(c2), key_buffer, n - l - 1, total_key_len, foreach_func);
|
||||
key_buffer[-1] = !invert;
|
||||
return dict_check_for_each(std::move(c2), key_buffer, n - l - 1, total_key_len, foreach_func, false, shuffle);
|
||||
}
|
||||
|
||||
bool DictionaryFixed::check_for_each(const foreach_func_t& foreach_func, bool invert_first) {
|
||||
bool DictionaryFixed::check_for_each(const foreach_func_t& foreach_func, bool invert_first, bool shuffle) {
|
||||
force_validate();
|
||||
if (is_empty()) {
|
||||
return true;
|
||||
}
|
||||
int key_len = get_key_bits();
|
||||
unsigned char key_buffer[max_key_bytes];
|
||||
return dict_check_for_each(get_root_cell(), td::BitPtr{key_buffer}, key_len, key_len, foreach_func, invert_first);
|
||||
return dict_check_for_each(get_root_cell(), td::BitPtr{key_buffer}, key_len, key_len, foreach_func, invert_first,
|
||||
shuffle);
|
||||
}
|
||||
|
||||
static inline bool set_bit(td::BitPtr ptr, bool value = true) {
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ class DictionaryFixed : public DictionaryBase {
|
|||
int get_common_prefix(td::BitPtr buffer, unsigned buffer_len);
|
||||
bool cut_prefix_subdict(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
|
||||
Ref<vm::Cell> extract_prefix_subdict_root(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
|
||||
bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false);
|
||||
bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false);
|
||||
int filter(filter_func_t check);
|
||||
bool combine_with(DictionaryFixed& dict2, const combine_func_t& combine_func, int mode = 0);
|
||||
bool combine_with(DictionaryFixed& dict2, const simple_combine_func_t& simple_combine_func, int mode = 0);
|
||||
|
|
@ -292,7 +292,7 @@ class DictionaryFixed : public DictionaryBase {
|
|||
std::pair<Ref<Cell>, bool> extract_prefix_subdict_internal(Ref<Cell> dict, td::ConstBitPtr prefix, int prefix_len,
|
||||
bool remove_prefix = false) const;
|
||||
bool dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer, int n, int total_key_len,
|
||||
const foreach_func_t& foreach_func, bool invert_first = false) const;
|
||||
const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false) const;
|
||||
std::pair<Ref<Cell>, int> dict_filter(Ref<Cell> dict, td::BitPtr key, int n, const filter_func_t& check_leaf,
|
||||
int& skip_rest) const;
|
||||
Ref<Cell> dict_combine_with(Ref<Cell> dict1, Ref<Cell> dict2, td::BitPtr key_buffer, int n, int total_key_len,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "openssl/digest.hpp"
|
||||
#include <sodium.h>
|
||||
#include "bls.h"
|
||||
#include "mc-config.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
|
|
@ -67,6 +68,10 @@ int exec_set_gas_generic(VmState* st, long long new_gas_limit) {
|
|||
throw VmNoGas{};
|
||||
}
|
||||
st->change_gas_limit(new_gas_limit);
|
||||
if (st->get_stop_on_accept_message()) {
|
||||
VM_LOG(st) << "External message is accepted, stopping TVM";
|
||||
return st->jump(td::Ref<QuitCont>{true, 0});
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +106,7 @@ void register_basic_gas_ops(OpcodeTable& cp0) {
|
|||
using namespace std::placeholders;
|
||||
cp0.insert(OpcodeInstr::mksimple(0xf800, 16, "ACCEPT", exec_accept))
|
||||
.insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit))
|
||||
.insert(OpcodeInstr::mksimple(0xf802, 16, "GASCONSUMED", exec_gas_consumed)->require_version(4))
|
||||
.insert(OpcodeInstr::mksimple(0xf807, 16, "GASCONSUMED", exec_gas_consumed)->require_version(4))
|
||||
.insert(OpcodeInstr::mksimple(0xf80f, 16, "COMMIT", exec_commit));
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +123,20 @@ static const StackEntry& get_param(VmState* st, unsigned idx) {
|
|||
return tuple_index(t1, idx);
|
||||
}
|
||||
|
||||
// ConfigParams: 18 (only one entry), 19, 20, 21, 24, 25, 43
|
||||
static td::Ref<CellSlice> get_unpacked_config_param(VmState* st, unsigned idx) {
|
||||
auto tuple = st->get_c7();
|
||||
auto t1 = tuple_index(tuple, 0).as_tuple_range(255);
|
||||
if (t1.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a tuple"};
|
||||
}
|
||||
auto t2 = tuple_index(t1, 14).as_tuple_range(255);
|
||||
if (t2.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a tuple"};
|
||||
}
|
||||
return tuple_index(t2, idx).as_slice();
|
||||
}
|
||||
|
||||
int exec_get_param(VmState* st, unsigned idx, const char* name) {
|
||||
if (name) {
|
||||
VM_LOG(st) << "execute " << name;
|
||||
|
|
@ -228,20 +247,150 @@ int exec_get_prev_blocks_info(VmState* st, unsigned idx, const char* name) {
|
|||
}
|
||||
|
||||
int exec_get_global_id(VmState* st) {
|
||||
Ref<Cell> config = get_param(st, 9).as_cell();
|
||||
if (config.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a cell"};
|
||||
VM_LOG(st) << "execute GLOBALID";
|
||||
if (st->get_global_version() >= 6) {
|
||||
Ref<CellSlice> cs = get_unpacked_config_param(st, 1);
|
||||
if (cs.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
|
||||
}
|
||||
if (cs->size() < 32) {
|
||||
throw VmError{Excno::cell_und, "invalid global-id config"};
|
||||
}
|
||||
st->get_stack().push_smallint(cs->prefetch_long(32));
|
||||
} else {
|
||||
Ref<Cell> config = get_param(st, 19).as_cell();
|
||||
if (config.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a cell"};
|
||||
}
|
||||
Dictionary config_dict{std::move(config), 32};
|
||||
Ref<Cell> cell = config_dict.lookup_ref(td::BitArray<32>{19});
|
||||
if (cell.is_null()) {
|
||||
throw VmError{Excno::unknown, "invalid global-id config"};
|
||||
}
|
||||
CellSlice cs = load_cell_slice(cell);
|
||||
if (cs.size() < 32) {
|
||||
throw VmError{Excno::unknown, "invalid global-id config"};
|
||||
}
|
||||
st->get_stack().push_smallint(cs.fetch_long(32));
|
||||
}
|
||||
Dictionary config_dict{std::move(config), 32};
|
||||
Ref<Cell> cell = config_dict.lookup_ref(td::BitArray<32>{19});
|
||||
if (cell.is_null()) {
|
||||
throw VmError{Excno::unknown, "invalid global-id config"};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static block::GasLimitsPrices get_gas_prices(VmState* st, bool is_masterchain) {
|
||||
Ref<CellSlice> cs = get_unpacked_config_param(st, is_masterchain ? 2 : 3);
|
||||
if (cs.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
|
||||
}
|
||||
CellSlice cs = load_cell_slice(cell);
|
||||
if (cs.size() < 32) {
|
||||
throw VmError{Excno::unknown, "invalid global-id config"};
|
||||
auto r_prices = block::Config::do_get_gas_limits_prices(*cs, is_masterchain ? 20 : 21);
|
||||
if (r_prices.is_error()) {
|
||||
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
|
||||
}
|
||||
st->get_stack().push_smallint(cs.fetch_long(32));
|
||||
return r_prices.move_as_ok();
|
||||
}
|
||||
|
||||
static block::MsgPrices get_msg_prices(VmState* st, bool is_masterchain) {
|
||||
Ref<CellSlice> cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
|
||||
if (cs.is_null()) {
|
||||
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
|
||||
}
|
||||
auto r_prices = block::Config::do_get_msg_prices(*cs, is_masterchain ? 24 : 25);
|
||||
if (r_prices.is_error()) {
|
||||
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
|
||||
}
|
||||
return r_prices.move_as_ok();
|
||||
}
|
||||
|
||||
int exec_get_gas_fee(VmState* st) {
|
||||
VM_LOG(st) << "execute GETGASFEE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
|
||||
stack.push_int(prices.compute_gas_price(gas));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_storage_fee(VmState* st) {
|
||||
VM_LOG(st) << "execute GETSTORAGEFEE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::int64 delta = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
Ref<CellSlice> cs = get_unpacked_config_param(st, 0);
|
||||
if (cs.is_null()) {
|
||||
// null means tat no StoragePrices is active, so the price is 0
|
||||
stack.push_smallint(0);
|
||||
return 0;
|
||||
}
|
||||
auto r_prices = block::Config::do_get_one_storage_prices(*cs);
|
||||
if (r_prices.is_error()) {
|
||||
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
|
||||
}
|
||||
block::StoragePrices prices = r_prices.move_as_ok();
|
||||
td::RefInt256 total;
|
||||
if (is_masterchain) {
|
||||
total = td::make_refint(cells) * prices.mc_cell_price;
|
||||
total += td::make_refint(bits) * prices.mc_bit_price;
|
||||
} else {
|
||||
total = td::make_refint(cells) * prices.cell_price;
|
||||
total += td::make_refint(bits) * prices.bit_price;
|
||||
}
|
||||
total *= delta;
|
||||
stack.push_int(td::rshift(total, 16, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_forward_fee(VmState* st) {
|
||||
VM_LOG(st) << "execute GETFORWARDFEE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
|
||||
stack.push_int(prices.compute_fwd_fees256(cells, bits));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_precompiled_gas(VmState* st) {
|
||||
VM_LOG(st) << "execute GETPRECOMPILEDGAS";
|
||||
Stack& stack = st->get_stack();
|
||||
stack.push_null();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_original_fwd_fee(VmState* st) {
|
||||
VM_LOG(st) << "execute GETORIGINALFWDFEE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::RefInt256 fwd_fee = stack.pop_int_finite();
|
||||
if (fwd_fee->sgn() < 0) {
|
||||
throw VmError{Excno::range_chk, "fwd_fee is negative"};
|
||||
}
|
||||
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
|
||||
stack.push_int(td::muldiv(fwd_fee, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_gas_fee_simple(VmState* st) {
|
||||
VM_LOG(st) << "execute GETGASFEESIMPLE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
|
||||
stack.push_int(td::rshift(td::make_refint(prices.gas_price) * gas, 16, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_forward_fee_simple(VmState* st) {
|
||||
VM_LOG(st) << "execute GETFORWARDFEESIMPLE";
|
||||
Stack& stack = st->get_stack();
|
||||
bool is_masterchain = stack.pop_bool();
|
||||
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
|
||||
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
|
||||
stack.push_int(td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16,
|
||||
1)); // divide by 2^16 with ceil rounding
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -259,13 +408,21 @@ void register_ton_config_ops(OpcodeTable& cp0) {
|
|||
.insert(OpcodeInstr::mksimple(0xf82b, 16, "INCOMINGVALUE", std::bind(exec_get_param, _1, 11, "INCOMINGVALUE")))
|
||||
.insert(OpcodeInstr::mksimple(0xf82c, 16, "STORAGEFEES", std::bind(exec_get_param, _1, 12, "STORAGEFEES")))
|
||||
.insert(OpcodeInstr::mksimple(0xf82d, 16, "PREVBLOCKSINFOTUPLE", std::bind(exec_get_param, _1, 13, "PREVBLOCKSINFOTUPLE")))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xf82e, 0xf830, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param))
|
||||
.insert(OpcodeInstr::mksimple(0xf82e, 16, "UNPACKEDCONFIGTUPLE", std::bind(exec_get_param, _1, 14, "UNPACKEDCONFIGTUPLE")))
|
||||
.insert(OpcodeInstr::mksimple(0xf82f, 16, "DUEPAYMENT", std::bind(exec_get_param, _1, 15, "DUEPAYMENT")))
|
||||
.insert(OpcodeInstr::mksimple(0xf830, 16, "CONFIGDICT", exec_get_config_dict))
|
||||
.insert(OpcodeInstr::mksimple(0xf832, 16, "CONFIGPARAM", std::bind(exec_get_config_param, _1, false)))
|
||||
.insert(OpcodeInstr::mksimple(0xf833, 16, "CONFIGOPTPARAM", std::bind(exec_get_config_param, _1, true)))
|
||||
.insert(OpcodeInstr::mksimple(0xf83400, 24, "PREVMCBLOCKS", std::bind(exec_get_prev_blocks_info, _1, 0, "PREVMCBLOCKS"))->require_version(4))
|
||||
.insert(OpcodeInstr::mksimple(0xf83401, 24, "PREVKEYBLOCK", std::bind(exec_get_prev_blocks_info, _1, 1, "PREVKEYBLOCK"))->require_version(4))
|
||||
.insert(OpcodeInstr::mksimple(0xf835, 16, "GLOBALID", exec_get_global_id)->require_version(4))
|
||||
.insert(OpcodeInstr::mksimple(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_fee)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf838, 16, "GETFORWARDFEE", exec_get_forward_fee)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf839, 16, "GETPRECOMPILEDGAS", exec_get_precompiled_gas)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf83a, 16, "GETORIGINALFWDFEE", exec_get_original_fwd_fee)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf83b, 16, "GETGASFEESIMPLE", exec_get_gas_fee_simple)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf83c, 16, "GETFORWARDFEESIMPLE", exec_get_forward_fee_simple)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf840, 16, "GETGLOBVAR", exec_get_global_var))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xf841, 0xf860, 16, 5, instr::dump_1c_and(31, "GETGLOB "), exec_get_global))
|
||||
.insert(OpcodeInstr::mksimple(0xf860, 16, "SETGLOBVAR", exec_set_global_var))
|
||||
|
|
@ -620,7 +777,6 @@ int exec_ristretto255_from_hash(VmState* st) {
|
|||
if (!x2->export_bytes(xb + 32, 32, false)) {
|
||||
throw VmError{Excno::range_chk, "x2 must fit in an unsigned 256-bit integer"};
|
||||
}
|
||||
CHECK(sodium_init() >= 0);
|
||||
crypto_core_ristretto255_from_hash(rb, xb);
|
||||
td::RefInt256 r{true};
|
||||
CHECK(r.write().import_bytes(rb, 32, false));
|
||||
|
|
@ -633,8 +789,7 @@ int exec_ristretto255_validate(VmState* st, bool quiet) {
|
|||
Stack& stack = st->get_stack();
|
||||
auto x = stack.pop_int();
|
||||
st->consume_gas(VmState::rist255_validate_gas_price);
|
||||
unsigned char xb[64];
|
||||
CHECK(sodium_init() >= 0);
|
||||
unsigned char xb[32];
|
||||
if (!x->export_bytes(xb, 32, false) || !crypto_core_ristretto255_is_valid_point(xb)) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
|
|
@ -656,7 +811,6 @@ int exec_ristretto255_add(VmState* st, bool quiet) {
|
|||
auto x = stack.pop_int();
|
||||
st->consume_gas(VmState::rist255_add_gas_price);
|
||||
unsigned char xb[32], yb[32], rb[32];
|
||||
CHECK(sodium_init() >= 0);
|
||||
if (!x->export_bytes(xb, 32, false) || !y->export_bytes(yb, 32, false) || crypto_core_ristretto255_add(rb, xb, yb)) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
|
|
@ -681,7 +835,6 @@ int exec_ristretto255_sub(VmState* st, bool quiet) {
|
|||
auto x = stack.pop_int();
|
||||
st->consume_gas(VmState::rist255_add_gas_price);
|
||||
unsigned char xb[32], yb[32], rb[32];
|
||||
CHECK(sodium_init() >= 0);
|
||||
if (!x->export_bytes(xb, 32, false) || !y->export_bytes(yb, 32, false) || crypto_core_ristretto255_sub(rb, xb, yb)) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
|
|
@ -719,17 +872,20 @@ int exec_ristretto255_mul(VmState* st, bool quiet) {
|
|||
auto n = stack.pop_int() % get_ristretto256_l();
|
||||
auto x = stack.pop_int();
|
||||
st->consume_gas(VmState::rist255_mul_gas_price);
|
||||
unsigned char xb[32], nb[32], rb[32];
|
||||
memset(rb, 255, sizeof(rb));
|
||||
CHECK(sodium_init() >= 0);
|
||||
if (!x->export_bytes(xb, 32, false) || !export_bytes_little(n, nb) || crypto_scalarmult_ristretto255(rb, nb, xb)) {
|
||||
if (std::all_of(rb, rb + 32, [](unsigned char c) { return c == 255; })) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
return 0;
|
||||
}
|
||||
throw VmError{Excno::range_chk, "invalid x or n"};
|
||||
if (n->sgn() == 0) {
|
||||
stack.push_smallint(0);
|
||||
if (quiet) {
|
||||
stack.push_bool(true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
unsigned char xb[32], nb[32], rb[32];
|
||||
if (!x->export_bytes(xb, 32, false) || !export_bytes_little(n, nb) || crypto_scalarmult_ristretto255(rb, nb, xb)) {
|
||||
if (quiet) {
|
||||
stack.push_bool(false);
|
||||
return 0;
|
||||
}
|
||||
throw VmError{Excno::range_chk, "invalid x or n"};
|
||||
}
|
||||
td::RefInt256 r{true};
|
||||
CHECK(r.write().import_bytes(rb, 32, false));
|
||||
|
|
@ -747,7 +903,6 @@ int exec_ristretto255_mul_base(VmState* st, bool quiet) {
|
|||
st->consume_gas(VmState::rist255_mulbase_gas_price);
|
||||
unsigned char nb[32], rb[32];
|
||||
memset(rb, 255, sizeof(rb));
|
||||
CHECK(sodium_init() >= 0);
|
||||
if (!export_bytes_little(n, nb) || crypto_scalarmult_ristretto255_base(rb, nb)) {
|
||||
if (std::all_of(rb, rb + 32, [](unsigned char c) { return c == 255; })) {
|
||||
if (quiet) {
|
||||
|
|
@ -833,7 +988,7 @@ int exec_bls_verify(VmState* st) {
|
|||
VM_LOG(st) << "execute BLS_VERIFY";
|
||||
Stack& stack = st->get_stack();
|
||||
stack.check_underflow(3);
|
||||
st->consume_gas(st->bls_verify_gas_price);
|
||||
st->consume_gas(VmState::bls_verify_gas_price);
|
||||
bls::P2 sig = slice_to_bls_p2(*stack.pop_cellslice());
|
||||
td::BufferSlice msg = slice_to_bls_msg(*stack.pop_cellslice());
|
||||
bls::P1 pub = slice_to_bls_p1(*stack.pop_cellslice());
|
||||
|
|
@ -845,8 +1000,7 @@ int exec_bls_aggregate(VmState* st) {
|
|||
VM_LOG(st) << "execute BLS_AGGREGATE";
|
||||
Stack& stack = st->get_stack();
|
||||
int n = stack.pop_smallint_range(stack.depth() - 1, 1);
|
||||
st->consume_gas(
|
||||
std::max(0LL, VmState::bls_aggregate_base_gas_price + (long long)n * VmState::bls_aggregate_element_gas_price));
|
||||
st->consume_gas(VmState::bls_aggregate_base_gas_price + (long long)n * VmState::bls_aggregate_element_gas_price);
|
||||
std::vector<bls::P2> sigs(n);
|
||||
for (int i = n - 1; i >= 0; --i) {
|
||||
sigs[i] = slice_to_bls_p2(*stack.pop_cellslice());
|
||||
|
|
@ -1591,17 +1745,39 @@ int exec_send_message(VmState* st) {
|
|||
}
|
||||
|
||||
bool is_masterchain = parse_addr_workchain(*my_addr) == -1 || (!ext_msg && parse_addr_workchain(*dest) == -1);
|
||||
Ref<Cell> config_dict = get_param(st, 9).as_cell();
|
||||
Dictionary config{config_dict, 32};
|
||||
Ref<Cell> prices_cell = config.lookup_ref(td::BitArray<32>{is_masterchain ? 24 : 25});
|
||||
block::gen::MsgForwardPrices::Record prices;
|
||||
if (prices_cell.is_null() || !tlb::unpack_cell(std::move(prices_cell), prices)) {
|
||||
td::Ref<CellSlice> prices_cs;
|
||||
if (st->get_global_version() >= 6) {
|
||||
prices_cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
|
||||
} else {
|
||||
Ref<Cell> config_dict = get_param(st, 9).as_cell();
|
||||
Dictionary config{config_dict, 32};
|
||||
Ref<Cell> prices_cell = config.lookup_ref(td::BitArray<32>{is_masterchain ? 24 : 25});
|
||||
if (prices_cell.not_null()) {
|
||||
prices_cs = load_cell_slice_ref(prices_cell);
|
||||
}
|
||||
}
|
||||
if (prices_cs.is_null()) {
|
||||
throw VmError{Excno::unknown, "invalid prices config"};
|
||||
}
|
||||
auto r_prices = block::Config::do_get_msg_prices(*prices_cs, is_masterchain ? 24 : 25);
|
||||
if (r_prices.is_error()) {
|
||||
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
|
||||
}
|
||||
block::MsgPrices prices = r_prices.move_as_ok();
|
||||
|
||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
||||
vm::VmStorageStat stat(1 << 13);
|
||||
td::uint64 max_cells;
|
||||
if (st->get_global_version() >= 6) {
|
||||
auto r_size_limits_config = block::Config::do_get_size_limits_config(get_unpacked_config_param(st, 6));
|
||||
if (r_size_limits_config.is_error()) {
|
||||
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_size_limits_config.error().message()};
|
||||
}
|
||||
max_cells = r_size_limits_config.ok().max_msg_cells;
|
||||
} else {
|
||||
max_cells = 1 << 13;
|
||||
}
|
||||
vm::VmStorageStat stat(max_cells);
|
||||
CellSlice cs = load_cell_slice(msg_cell);
|
||||
cs.skip_first(cs.size());
|
||||
stat.add_storage(cs);
|
||||
|
|
@ -1649,7 +1825,7 @@ int exec_send_message(VmState* st) {
|
|||
if (ihr_disabled) {
|
||||
ihr_fee_short = 0;
|
||||
} else {
|
||||
ihr_fee_short = td::uint128(fwd_fee_short).mult(prices.ihr_price_factor).shr(16).lo();
|
||||
ihr_fee_short = td::uint128(fwd_fee_short).mult(prices.ihr_factor).shr(16).lo();
|
||||
}
|
||||
fwd_fee = td::RefInt256{true, fwd_fee_short};
|
||||
ihr_fee = td::RefInt256{true, ihr_fee_short};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include "vm/dict.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/vm.h"
|
||||
#include "cp0.h"
|
||||
#include <sodium.h>
|
||||
|
||||
namespace vm {
|
||||
|
||||
|
|
@ -770,4 +772,15 @@ void VmState::restore_parent_vm(int res) {
|
|||
}
|
||||
}
|
||||
|
||||
td::Status init_vm(bool enable_debug) {
|
||||
if (!init_op_cp0(enable_debug)) {
|
||||
return td::Status::Error("Failed to init TVM: failed to init cp0");
|
||||
}
|
||||
auto code = sodium_init();
|
||||
if (code < 0) {
|
||||
return td::Status::Error(PSTRING() << "Failed to init TVM: sodium_init, code=" << code);
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class VmState final : public VmStateInterface {
|
|||
td::HashSet<CellHash> loaded_cells;
|
||||
int stack_trace{0}, debug_off{0};
|
||||
bool chksig_always_succeed{false};
|
||||
bool stop_on_accept_message{false};
|
||||
td::optional<td::Bits256> missing_library;
|
||||
td::uint16 max_data_depth = 512; // Default value
|
||||
int global_version{0};
|
||||
|
|
@ -339,7 +340,7 @@ class VmState final : public VmStateInterface {
|
|||
void preclear_cr(const ControlRegs& save) {
|
||||
cr &= save;
|
||||
}
|
||||
int get_global_version() const {
|
||||
int get_global_version() const override {
|
||||
return global_version;
|
||||
}
|
||||
void set_global_version(int version) {
|
||||
|
|
@ -381,6 +382,12 @@ class VmState final : public VmStateInterface {
|
|||
bool get_chksig_always_succeed() const {
|
||||
return chksig_always_succeed;
|
||||
}
|
||||
void set_stop_on_accept_message(bool flag) {
|
||||
stop_on_accept_message = flag;
|
||||
}
|
||||
bool get_stop_on_accept_message() const {
|
||||
return stop_on_accept_message;
|
||||
}
|
||||
Ref<OrdCont> ref_to_cont(Ref<Cell> cell) const {
|
||||
return td::make_ref<OrdCont>(load_cell_slice_ref(std::move(cell)), get_cp());
|
||||
}
|
||||
|
|
@ -423,4 +430,6 @@ int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* d
|
|||
|
||||
Ref<vm::Cell> lookup_library_in(td::ConstBitPtr key, Ref<vm::Cell> lib_root);
|
||||
|
||||
td::Status init_vm(bool enable_debug = false);
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "vm/cells.h"
|
||||
#include "common/global-version.h"
|
||||
|
||||
#include "td/utils/Context.h"
|
||||
|
||||
|
|
@ -38,6 +39,9 @@ class VmStateInterface : public td::Context<VmStateInterface> {
|
|||
virtual bool register_op(int op_units = 1) {
|
||||
return true;
|
||||
};
|
||||
virtual int get_global_version() const {
|
||||
return ton::SUPPORTED_VERSION;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue