2
This file is part of TON Blockchain source code.
4
TON Blockchain is free software; you can redistribute it and/or
5
modify it under the terms of the GNU General Public License
6
as published by the Free Software Foundation; either version 2
7
of the License, or (at your option) any later version.
9
TON Blockchain is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
17
In addition, as a special exception, the copyright holders give permission
18
to link the code of portions of this program with the OpenSSL library.
19
You must obey the GNU General Public License in all respects for all
20
of the code used other than OpenSSL. If you modify file(s) with this
21
exception, you may extend this exception to your version of the file(s),
22
but you are not obligated to do so. If you do not wish to do so, delete this
23
exception statement from your version. If you delete this exception statement
24
from all source files in the program, then also delete it here.
26
Copyright 2017-2020 Telegram Systems LLP
44
#include "vm/stack.hpp"
48
#include "fift/Dictionary.h"
49
#include "fift/SourceLookup.h"
50
#include "fift/IntCtx.h"
51
#include "fift/words.h"
53
#include "td/utils/logging.h"
54
#include "td/utils/misc.h"
55
#include "td/utils/Parser.h"
56
#include "td/utils/port/path.h"
57
#include "td/utils/port/signals.h"
60
#include "block-parse.h"
61
#include "block-auto.h"
65
#if defined(_INTERNAL_COMPILE) || defined(_TONLIB_COMPILE)
67
#include "tonlib/keys/Mnemonic.h"
74
#define THRERR(__msg) \
76
throw fift::IntError{__msg}; \
87
enum { wc_master = -1, wc_base = 0 };
88
constexpr int wc_undef = std::numeric_limits<int>::min();
90
int workchain_id = wc_undef;
93
typedef td::BitArray<256> hash_t;
99
td::RefInt256 gram_balance;
100
Ref<vm::DataCell> state_init; // StateInit
101
Ref<vm::DataCell> account; // Account
102
SmcDescr(const hash_t& _addr) : addr(_addr), split_depth(0), preinit_only(false) {
106
std::map<hash_t, SmcDescr> smart_contracts;
107
td::RefInt256 total_smc_balance{true, 0}, max_total_smc_balance;
109
struct PublicLibDescr {
111
std::set<hash_t> publishers;
112
PublicLibDescr(Ref<vm::Cell> _root) : root(std::move(_root)) {
116
std::map<hash_t, PublicLibDescr> public_libraries;
119
Ref<vm::Cell> config_param_root;
121
vm::Dictionary config_dict{32};
125
bool set_config_smc(const SmcDescr& smc) {
126
if (config_addr_set || smc.preinit_only || workchain_id != wc_master || smc.split_depth) {
129
vm::CellSlice cs = load_cell_slice(smc.state_init);
131
PDO(block::gen::t_Maybe_natwidth_5.skip(cs) && block::gen::t_Maybe_TickTock.skip(cs) &&
132
block::gen::t_Maybe_Ref_Cell.skip(cs));
135
PDO(cs.fetch_ulong(1) == 1 && cs.fetch_ref_to(data));
136
THRERR("config smart contract must have non-empty data");
137
vm::CellSlice cs2 = load_cell_slice(data);
138
PDO(cs2.fetch_ref_to(data));
139
THRERR("first reference in config smart contract data must point to initial configuration");
140
PDO(block::valid_config_data(data, smc.addr));
141
THRERR("invalid smart contract configuration data");
142
config_addr = smc.addr;
143
config_param_root = std::move(data);
144
config_addr_set = true;
146
std::cerr << "set smart contract " << config_addr << " as the configuration smart contract with configuration:\n";
147
load_cell_slice(config_param_root).print_rec(std::cerr);
152
void interpret_set_workchain(vm::Stack& stack) {
153
workchain_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
156
void interpret_get_workchain(vm::Stack& stack) {
157
stack.push_smallint(workchain_id);
160
void interpret_set_global_id(vm::Stack& stack) {
161
global_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
164
void interpret_get_global_id(vm::Stack& stack) {
165
stack.push_smallint(global_id);
168
void interpret_get_verbosity(vm::Stack& stack) {
169
stack.push_smallint(GET_VERBOSITY_LEVEL());
172
void interpret_set_verbosity(vm::Stack& stack) {
173
int x = stack.pop_smallint_range(15);
174
SET_VERBOSITY_LEVEL(x);
177
void interpret_set_config_smartcontract(vm::Stack& stack) {
178
if (workchain_id != wc_master) {
179
throw fift::IntError{"configuration smart contract may be selected in masterchain only"};
181
if (config_addr_set) {
182
throw fift::IntError{"configuration smart contract already selected"};
184
td::RefInt256 int_addr = stack.pop_int_finite();
186
if (!int_addr->export_bits(addr.bits(), 256, false)) {
187
throw fift::IntError{"not a valid smart-contract address"};
189
auto it = smart_contracts.find(addr);
190
if (it == smart_contracts.end()) {
191
throw fift::IntError{"unknown smart contract"};
193
const SmcDescr& smc = it->second;
194
assert(smc.addr == addr);
195
if (smc.preinit_only) {
196
throw fift::IntError{"configuration smart contract must be completely initialized"};
198
if (!set_config_smc(smc)) {
199
throw fift::IntError{"invalid configuration smart contract"};
203
bool is_empty_cell(Ref<vm::Cell> cell) {
205
auto cs = load_cell_slice_special(std::move(cell), is_special);
206
return !is_special && cs.empty_ext();
209
bool add_public_library(hash_t lib_addr, hash_t smc_addr, Ref<vm::Cell> lib_root) {
210
if (lib_root.is_null() || lib_root->get_hash().as_array() != lib_addr.as_array()) {
213
auto ins = public_libraries.emplace(lib_addr, lib_root);
214
PublicLibDescr& lib = ins.first->second;
215
lib.publishers.insert(smc_addr);
217
std::cerr << "added " << (ins.second ? "new " : "") << "public library " << lib_addr << " with publisher "
218
<< smc_addr << std::endl;
223
td::RefInt256 create_smartcontract(td::RefInt256 smc_addr, Ref<vm::Cell> code, Ref<vm::Cell> data,
224
Ref<vm::Cell> library, td::RefInt256 balance, int special, int split_depth,
226
if (is_empty_cell(code)) {
229
if (is_empty_cell(data)) {
232
if (is_empty_cell(library)) {
236
if (library.not_null()) {
237
PDO(block::valid_library_collection(library, false));
238
THRERR("not a valid library collection");
242
PDO(cb.store_long_bool(0, 1));
244
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(split_depth, 5));
246
THRERR("invalid split_depth for a smart contract");
248
PDO(cb.store_long_bool(0, 1));
250
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(special, 2));
252
THRERR("invalid special TickTock argument for a smart contract");
253
PDO(cb.store_maybe_ref(std::move(code)) && cb.store_maybe_ref(std::move(data)) && cb.store_maybe_ref(library));
254
THRERR("cannot store smart-contract code, data or library");
255
Ref<vm::DataCell> state_init = cb.finalize();
257
if (smc_addr.is_null()) {
258
addr = state_init->get_hash().as_array();
259
smc_addr = td::RefInt256{true};
260
PDO(smc_addr.write().import_bits(addr.data(), 0, 256, false));
261
} else if (mode == 1) {
262
throw fift::IntError{"cannot create uninitialized smart contracts with specified addresses"};
264
PDO(smc_addr->export_bits(addr.data(), 0, 256, false));
266
THRERR("cannot initialize smart-contract address");
268
std::cerr << "smart-contract address is ";
269
std::cerr << addr << " = " << smc_addr << std::endl;
271
PDO(mode || !sgn(balance));
272
THRERR("cannot set non-zero balance to smart contract unless it is initialized");
273
PDO(sgn(balance) >= 0);
274
THRERR("balance cannot be negative");
277
std::cerr << "StateInit used for computing address: ";
278
block::gen::t_StateInit.print_ref(std::cerr, state_init);
280
return smc_addr; // compute address only
282
auto it = smart_contracts.find(addr);
283
if (it != smart_contracts.end()) {
284
std::cerr << "smart contract " << addr << " already defined\n";
285
throw fift::IntError{"smart contract already exists"};
287
auto ins = smart_contracts.emplace(addr, addr);
289
SmcDescr& smc = ins.first->second;
290
smc.split_depth = split_depth;
291
smc.preinit_only = (mode == 1);
292
smc.gram_balance = balance;
293
total_smc_balance += balance;
295
smc.state_init = std::move(state_init);
297
if (max_total_smc_balance.not_null() && total_smc_balance > max_total_smc_balance) {
298
throw fift::IntError{"total smart-contract balance exceeds limit"};
301
PDO(cb.store_long_bool(0, 64) // account_storage$_ last_trans_lt:uint64
302
&& block::tlb::t_Grams.store_integer_value(cb, *balance) // balance.grams:Grams
303
&& cb.store_long_bool(0, 1)); // balance.other:ExtraCurrencyCollection
305
PDO(block::gen::t_AccountState.pack_account_uninit(cb));
307
PDO(block::gen::t_AccountState.pack_account_active(cb, vm::load_cell_slice_ref(smc.state_init)));
309
THRERR("cannot create smart-contract AccountStorage");
310
Ref<vm::DataCell> storage = cb.finalize();
311
vm::CellStorageStat stats;
312
PDO(stats.compute_used_storage(Ref<vm::Cell>(storage)).is_ok());
314
std::cerr << "storage is:\n";
315
vm::load_cell_slice(storage).print_rec(std::cerr);
316
std::cerr << "stats: bits=" << stats.bits << ", cells=" << stats.cells << std::endl;
317
std::cerr << "block::gen::AccountStorage.validate_ref() = " << block::gen::t_AccountStorage.validate_ref(storage)
319
std::cerr << "block::tlb::AccountStorage.validate_ref() = " << block::tlb::t_AccountStorage.validate_ref(storage)
322
PDO(block::gen::t_AccountStorage.validate_ref(storage));
323
THRERR("AccountStorage of created smart-contract is invalid (?)");
324
cb.reset(); // build Account
325
PDO(cb.store_long_bool(1, 1)); // account$1
326
int ctor = 3; // addr_var$11
327
if (workchain_id >= -128 && workchain_id <= 127) {
328
ctor = 2; // addr_std$10
330
PDO(cb.store_long_bool(ctor, 2)); // addr_std$10 or addr_var$11
332
PDO(cb.store_long_bool(1, 1) // just$1
333
&& cb.store_ulong_rchk_bool(split_depth, 5) // depth:(## 5)
334
&& cb.store_bits_bool(addr.cbits(), split_depth)); // rewrite pfx:(depth * Bit)
336
PDO(cb.store_long_bool(0, 1)); // nothing$0
338
PDO(cb.store_long_rchk_bool(workchain_id, ctor == 2 ? 8 : 32) && cb.store_bits_bool(addr.cbits(), 256));
339
THRERR("Cannot serialize addr:MsgAddressInt of the new smart contract");
340
// storage_stat:StorageInfo -> storage_stat.used:StorageUsed
341
PDO(block::store_UInt7(cb, stats.cells) // cells:(VarUInteger 7)
342
&& block::store_UInt7(cb, stats.bits) // bits:(VarUInteger 7)
343
&& block::store_UInt7(cb, stats.public_cells)); // public_cells:(VarUInteger 7)
344
THRERR("Cannot serialize used:StorageUsed of the new smart contract");
345
PDO(cb.store_long_bool(0, 33)); // last_paid:uint32 due_payment:(Maybe Grams)
346
PDO(cb.append_data_cell_bool(storage)); // storage:AccountStorage
347
THRERR("Cannot create Account of the new smart contract");
348
smc.account = cb.finalize();
350
std::cerr << "account is:\n";
351
vm::load_cell_slice(smc.account).print_rec(std::cerr);
352
std::cerr << "block::gen::Account.validate_ref() = " << block::gen::t_Account.validate_ref(smc.account)
354
std::cerr << "block::tlb::Account.validate_ref() = " << block::tlb::t_Account.validate_ref(smc.account)
357
PDO(block::gen::t_Account.validate_ref(smc.account));
358
THRERR("Account of created smart contract is invalid (?)");
359
if (library.not_null()) {
360
vm::Dictionary dict{std::move(library), 256};
361
ok &= dict.check_for_each([addr](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) -> bool {
362
return !cs->prefetch_ulong(1) || add_public_library(key, addr, cs->prefetch_ref());
364
THRERR("Error processing libraries published by new smart contract");
369
// stores accounts:ShardAccounts
370
bool store_accounts(vm::CellBuilder& cb) {
371
vm::AugmentedDictionary dict{256, block::tlb::aug_ShardAccounts};
372
for (const auto& smc_pair : smart_contracts) {
373
const SmcDescr& smc = smc_pair.second;
374
CHECK(smc_pair.first == smc.addr);
376
bool ok = cb.store_ref_bool(smc.account) // account_descr$_ acc:^Account
377
&& cb.store_zeroes_bool(256 + 64) // last_trans_hash:bits256 last_trans_lt:uint64
378
&& dict.set_builder(smc.addr.cbits(), 256, cb, vm::Dictionary::SetMode::Add);
381
return std::move(dict).append_dict_to_bool(cb);
384
// stores libraries:(HashmapE 256 LibDescr)
385
bool store_public_libraries(vm::CellBuilder& cb) {
386
vm::Dictionary dict{256};
388
vm::CellBuilder empty_cb;
389
for (const auto& lib_pair : public_libraries) {
390
const PublicLibDescr pl = lib_pair.second;
391
PDO(pl.root->get_hash().as_array() == lib_pair.first.as_array());
392
vm::Dictionary publishers{256};
393
for (const auto& publisher : pl.publishers) {
394
PDO(publishers.set_builder(publisher.cbits(), 256, empty_cb, vm::Dictionary::SetMode::Add));
396
Ref<vm::Cell> root = std::move(publishers).extract_root_cell();
397
PDO(root.not_null());
398
THRERR("public library has an empty or invalid set of publishers");
399
vm::CellBuilder value_cb; // LibDescr
400
PDO(value_cb.store_long_bool(0, 2) && value_cb.store_ref_bool(pl.root) &&
401
value_cb.append_cellslice_bool(vm::load_cell_slice(std::move(root))));
402
THRERR("cannot create LibDescr for a public library");
403
PDO(dict.set_builder(lib_pair.first.cbits(), 256, value_cb, vm::Dictionary::SetMode::Add));
404
THRERR("cannot insert LibDescr of a public library into the public library collection");
406
PDO(std::move(dict).append_dict_to_bool(cb));
410
// stores config:ConfigParams
411
bool store_config_params(vm::CellBuilder& cb) {
412
return config_addr_set && config_param_root.not_null() &&
413
cb.store_bits_bool(config_addr.cbits(), 256) // _ config_addr:bits256
414
&& cb.store_ref_bool(config_param_root); // config:^(Hashmap 32 ^Cell)
417
// stores hash of initial masterchain validator set computed from configuration parameter 34
418
bool store_validator_list_hash(vm::CellBuilder& cb) {
419
Ref<vm::Cell> vset_cell = config_dict.lookup_ref(td::BitArray<32>{34});
420
auto res = block::Config::unpack_validator_set(std::move(vset_cell));
421
if (res.is_error()) {
422
LOG(ERROR) << "cannot unpack current validator set: " << res.move_as_error().to_string();
425
auto vset = res.move_as_ok();
426
LOG_CHECK(vset) << "unpacked validator set is empty";
427
auto ccvc = block::Config::unpack_catchain_validators_config(config_dict.lookup_ref(td::BitArray<32>{28}));
428
ton::ShardIdFull shard{ton::masterchainId};
429
auto nodes = block::Config::do_compute_validator_set(ccvc, shard, *vset, now, 0);
430
LOG_CHECK(!nodes.empty()) << "validator node list in unpacked validator set is empty";
431
auto vset_hash = block::compute_validator_set_hash(0, shard, std::move(nodes));
432
LOG(DEBUG) << "initial validator set hash is " << vset_hash;
433
return cb.store_long_bool(vset_hash, 32);
436
// stores custom:(Maybe ^McStateExtra)
437
bool store_custom(vm::CellBuilder& cb) {
438
if (workchain_id != wc_master) {
439
return cb.store_long_bool(0, 1); // nothing
441
vm::CellBuilder cb2, cb3;
443
PDO(cb2.store_long_bool(0xcc26, 16) // masterchain_state_extra#cc26
444
&& cb2.store_long_bool(0, 1) // shard_hashes:ShardHashes = (HashmapE 32 ^(BinTree ShardDescr))
445
&& store_config_params(cb2) // config:ConfigParams
446
&& cb3.store_long_bool(0, 16) // ^[ flags:(## 16) { flags = 0 }
447
&& store_validator_list_hash(cb3) // validator_list_hash_short:uint32
448
&& cb3.store_long_bool(0, 32) // catchain_seqno:uint32
449
&& cb3.store_bool_bool(true) // nx_cc_updated:Bool
450
&& cb3.store_zeroes_bool(1 + 65) // prev_blocks:OldMcBlocksInfo
451
&& cb3.store_long_bool(2, 1 + 1) // after_key_block:Bool last_key_block:(Maybe ...)
452
&& cb2.store_ref_bool(cb3.finalize()) // ]
453
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // global_balance:CurrencyCollection
454
&& cb.store_long_bool(1, 1) // just
455
&& cb.store_ref_bool(cb2.finalize()));
459
Ref<vm::Cell> create_state() {
460
vm::CellBuilder cb, cb2;
461
now = static_cast<ton::UnixTime>(time(0));
463
PDO(workchain_id != wc_undef);
464
THRERR("workchain_id is unset, cannot generate state");
465
PDO(workchain_id != wc_master || config_addr_set);
466
THRERR("configuration smart contract must be selected");
467
PDO(cb.store_long_bool(0x9023afe2, 32) // shard_state#9023afe2
468
&& cb.store_long_bool(global_id, 32)); // global_id:int32
469
PDO(cb.store_long_bool(0, 8) && cb.store_long_bool(workchain_id, 32) &&
470
cb.store_long_bool(0, 64) // shard_id:ShardIdent
471
&& cb.store_long_bool(0, 32) // seq_no:#
472
&& cb.store_zeroes_bool(32) // vert_seq_no:#
473
&& cb.store_long_bool(now, 32) // gen_utime:uint32
474
&& cb.store_zeroes_bool(64) // gen_lt:uint64
475
&& cb.store_ones_bool(32) // min_ref_mc_seqno:uint32
476
&& cb2.store_zeroes_bool(1 + 64 + 2) // OutMsgQueueInfo
477
&& cb.store_ref_bool(cb2.finalize()) // out_msg_queue_info:^OutMsgQueueInfo
478
&& cb.store_long_bool(0, 1) // before_split:Bool
479
&& store_accounts(cb2) // accounts:^ShardAccounts
480
&& cb.store_ref_bool(cb2.finalize()) // ...
481
&& cb2.store_zeroes_bool(128) // ^[ overload_history:uint64 underload_history:uint64
482
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // total_balance:CurrencyCollection
483
&& block::tlb::t_CurrencyCollection.null_value(cb2) // total_validator_fees:CurrencyCollection
484
&& store_public_libraries(cb2) // libraries:(Hashmap 256 LibDescr)
485
&& cb2.store_long_bool(0, 1) // master_ref:(Maybe BlkMasterInfo)
486
&& cb.store_ref_bool(cb2.finalize()) // ]
487
&& store_custom(cb)); // custom:(Maybe ^McStateExtra)
488
THRERR("cannot create blockchain state");
489
Ref<vm::Cell> cell = cb.finalize();
491
std::cerr << "shard_state is:\n";
492
vm::load_cell_slice(cell).print_rec(std::cerr);
493
std::cerr << "pretty-printed shard_state is:\n";
494
block::gen::t_ShardState.print_ref(std::cerr, cell);
496
std::cerr << "block::gen::ShardState.validate_ref() = " << block::gen::t_ShardState.validate_ref(cell) << std::endl;
497
std::cerr << "block::tlb::ShardState.validate_ref() = " << block::tlb::t_ShardState.validate_ref(cell) << std::endl;
498
block::gen::ShardStateUnsplit::Record data;
499
bool ok1 = tlb::unpack_cell(cell, data);
500
std::cerr << "block::gen::ShardState.unpack_cell() = " << ok1 << std::endl;
502
std::cerr << "shard_id = " << data.shard_id
503
<< "; out_msg_queue_info = " << load_cell_slice(data.out_msg_queue_info)
504
<< "; total_balance = " << data.r1.total_balance << std::endl;
507
PDO(block::gen::t_ShardState.validate_ref(cell));
508
PDO(block::tlb::t_ShardState.validate_ref(cell));
509
THRERR("created an invalid ShardState record");
517
// split_depth (int 0..32)
518
// special (int 0..3, +2 = tick, +1 = tock)
519
// [ address (uint256) ]
520
// mode (0 = compute address only, 1 = create uninit, 2 = create complete; +4 = with specified address)
521
// --> 256-bit address
522
void interpret_register_smartcontract(vm::Stack& stack) {
523
if (workchain_id == wc_undef) {
524
throw fift::IntError{"cannot register a smartcontract unless the workchain is specified first"};
526
td::RefInt256 spec_addr;
527
int mode = stack.pop_smallint_range(2 + 4); // allowed modes: 0 1 2 4 5 6
529
throw fift::IntError{"invalid mode"};
532
spec_addr = stack.pop_int_finite();
535
int special = stack.pop_smallint_range(3);
536
if (special && workchain_id != wc_master) {
537
throw fift::IntError{"cannot create special smartcontracts outside of the masterchain"};
539
int split_depth = stack.pop_smallint_range(32);
540
td::RefInt256 balance = stack.pop_int_finite();
541
if (sgn(balance) < 0) {
542
throw fift::IntError{"initial balance of a smartcontract cannot be negative"};
544
if (sgn(balance) > 0 && !mode) {
545
throw fift::IntError{"cannot set non-zero balance if an account is not created"};
547
Ref<vm::Cell> library = stack.pop_cell();
548
Ref<vm::Cell> data = stack.pop_cell();
549
Ref<vm::Cell> code = stack.pop_cell();
550
td::RefInt256 addr = create_smartcontract(std::move(spec_addr), std::move(code), std::move(data), std::move(library),
551
std::move(balance), special, split_depth, mode);
552
if (addr.is_null()) {
553
throw fift::IntError{"internal error while creating smartcontract"};
555
stack.push(std::move(addr));
558
void interpret_create_state(vm::Stack& stack) {
560
throw fift::IntError{
561
"(global) blockchain id must be set to a non-zero value: negative for test chains, positive for production"};
563
Ref<vm::Cell> state = create_state();
564
if (state.is_null()) {
565
throw fift::IntError{"could not create blockchain state"};
567
stack.push(std::move(state));
570
void interpret_get_config_dict(vm::Stack& stack) {
571
Ref<vm::Cell> value = config_dict.get_root_cell();
572
if (value.is_null()) {
573
stack.push_bool(false);
575
stack.push_cell(std::move(value));
576
stack.push_bool(true);
580
void interpret_get_config_param(vm::Stack& stack) {
581
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
582
Ref<vm::Cell> value = config_dict.lookup_ref(td::BitArray<32>{x});
583
if (value.is_null()) {
584
stack.push_bool(false);
586
stack.push_cell(std::move(value));
587
stack.push_bool(true);
591
void interpret_set_config_param(vm::Stack& stack) {
592
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
593
Ref<vm::Cell> value = stack.pop_cell();
594
if (verbosity > 2 && x >= 0) {
595
std::cerr << "setting configuration parameter #" << x << " to ";
596
// vm::load_cell_slice(value).print_rec(std::cerr);
597
block::gen::ConfigParam{x}.print_ref(std::cerr, value);
598
std::cerr << std::endl;
600
if (x >= 0 && !block::gen::ConfigParam{x}.validate_ref(value)) {
601
throw fift::IntError{"invalid value for indicated configuration parameter"};
603
if (!config_dict.set_ref(td::BitArray<32>{x}, std::move(value))) {
604
throw fift::IntError{"cannot set value of configuration parameter (value too long?)"};
608
void interpret_check_config_param(vm::Stack& stack) {
609
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
610
Ref<vm::Cell> value = stack.pop_cell();
611
if (verbosity > 2 && x >= 0) {
612
std::cerr << "checking validity as configuration parameter #" << x << " of ";
613
// vm::load_cell_slice(value).print_rec(std::cerr);
614
block::gen::ConfigParam{x}.print_ref(std::cerr, value);
615
std::cerr << std::endl;
617
stack.push_bool(x < 0 || block::gen::ConfigParam{x}.validate_ref(value));
620
void interpret_is_shard_state(vm::Stack& stack) {
621
Ref<vm::Cell> cell = stack.pop_cell();
623
std::cerr << "custom shard state is:\n";
624
vm::load_cell_slice(cell).print_rec(std::cerr);
625
std::cerr << "pretty-printed custom shard state is:\n";
626
block::gen::t_ShardState.print_ref(std::cerr, cell);
628
stack.push_bool(block::gen::t_ShardState.validate_ref(std::move(cell)));
631
void interpret_is_workchain_descr(vm::Stack& stack) {
632
Ref<vm::Cell> cell = stack.pop_cell();
634
std::cerr << "WorkchainDescr is:\n";
635
vm::load_cell_slice(cell).print_rec(std::cerr);
636
std::cerr << "pretty-printed WorkchainDescr is:\n";
637
block::gen::t_WorkchainDescr.print_ref(std::cerr, cell);
639
stack.push_bool(block::gen::t_WorkchainDescr.validate_ref(std::move(cell)));
642
void interpret_add_extra_currencies(vm::Stack& stack) {
643
Ref<vm::Cell> y = stack.pop_maybe_cell(), x = stack.pop_maybe_cell(), res;
644
bool ok = block::add_extra_currency(std::move(x), std::move(y), res);
646
stack.push_maybe_cell(std::move(res));
651
void interpret_sub_extra_currencies(vm::Stack& stack) {
652
Ref<vm::Cell> y = stack.pop_maybe_cell(), x = stack.pop_maybe_cell(), res;
653
bool ok = block::sub_extra_currency(std::move(x), std::move(y), res);
655
stack.push_maybe_cell(std::move(res));
660
void interpret_allocated_balance(vm::Stack& stack) {
661
stack.push_int(total_smc_balance);
665
void interpret_mnemonic_to_privkey(vm::Stack& stack, int mode) {
666
td::SecureString str{td::Slice{stack.pop_string()}};
667
auto res = tonlib::Mnemonic::create(std::move(str), td::SecureString());
668
if (res.is_error()) {
669
throw fift::IntError{res.move_as_error().to_string()};
671
auto privkey = res.move_as_ok().to_private_key();
672
td::SecureString key;
674
auto pub = privkey.get_public_key();
675
key = pub.move_as_ok().as_octet_string();
677
key = privkey.as_octet_string();
679
stack.push_bytes(key.as_slice());
683
void init_words_custom(fift::Dictionary& d) {
684
using namespace std::placeholders;
685
d.def_stack_word("verb@ ", interpret_get_verbosity);
686
d.def_stack_word("verb! ", interpret_set_verbosity);
687
d.def_stack_word("wcid@ ", interpret_get_workchain);
688
d.def_stack_word("wcid! ", interpret_set_workchain);
689
d.def_stack_word("globalid@ ", interpret_get_global_id);
690
d.def_stack_word("globalid! ", interpret_set_global_id);
691
d.def_stack_word("config@ ", interpret_get_config_param);
692
d.def_stack_word("config! ", interpret_set_config_param);
693
d.def_stack_word("config-valid? ", interpret_check_config_param);
694
d.def_stack_word("(configdict) ", interpret_get_config_dict);
695
d.def_stack_word("register_smc ", interpret_register_smartcontract);
696
d.def_stack_word("set_config_smc ", interpret_set_config_smartcontract);
697
d.def_stack_word("create_state ", interpret_create_state);
698
d.def_stack_word("isShardState? ", interpret_is_shard_state);
699
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
700
d.def_stack_word("CC+? ", interpret_add_extra_currencies);
701
d.def_stack_word("CC-? ", interpret_sub_extra_currencies);
702
d.def_stack_word("allocated-balance ", interpret_allocated_balance);
704
d.def_stack_word("mnemo>priv ", std::bind(interpret_mnemonic_to_privkey, _1, 0));
705
d.def_stack_word("mnemo>pub ", std::bind(interpret_mnemonic_to_privkey, _1, 1));
709
tlb::TypenameLookup tlb_dict;
711
// ( S -- T -1 or 0 ) Looks up TLB type by name
712
void interpret_tlb_type_lookup(vm::Stack& stack) {
713
auto ptr = tlb_dict.lookup(stack.pop_string());
715
stack.push_make_object<tlb::TlbTypeHolder>(ptr);
717
stack.push_bool(ptr);
720
td::Ref<tlb::TlbTypeHolder> pop_tlb_type(vm::Stack& stack) {
721
auto res = stack.pop_object<tlb::TlbTypeHolder>();
723
throw vm::VmError{vm::Excno::type_chk, "not a TLB type"};
728
// ( T -- S ) Gets TLB type name
729
void interpret_tlb_type_name(vm::Stack& stack) {
730
stack.push_string((*pop_tlb_type(stack))->get_type_name());
733
// ( T -- ) Prints TLB type name
734
void interpret_print_tlb_type(vm::Stack& stack) {
735
std::cout << (*pop_tlb_type(stack))->get_type_name();
738
// ( s T -- ) Dumps (part of) slice s as a value of TLB type T
739
void interpret_tlb_dump_as(vm::Stack& stack) {
740
auto tp = pop_tlb_type(stack);
741
(*tp)->print(std::cout, stack.pop_cellslice());
744
// ( s T -- s' S -1 or 0 )
745
// Detects prefix of slice s that is a value of TLB type T, returns the remainder as s', and prints the value into String S.
746
void interpret_tlb_dump_to_str(vm::Stack& stack) {
747
auto tp = pop_tlb_type(stack);
748
auto cs = stack.pop_cellslice();
749
std::ostringstream os;
750
bool ok = (*tp)->print_skip(os, cs.write());
752
stack.push(std::move(cs));
753
stack.push_string(os.str());
758
// ( s T -- s' -1 or 0 ) Skips the only prefix of slice s that can be a value of TLB type T
759
void interpret_tlb_skip(vm::Stack& stack) {
760
auto tp = pop_tlb_type(stack);
761
auto cs = stack.pop_cellslice();
762
bool ok = (*tp)->skip(cs.write());
764
stack.push(std::move(cs));
769
// ( s T -- s' -1 or 0 ) Checks whether a prefix of slice s is a valid value of TLB type T, and skips it
770
void interpret_tlb_validate_skip(vm::Stack& stack) {
771
auto tp = pop_tlb_type(stack);
772
auto cs = stack.pop_cellslice();
773
bool ok = (*tp)->validate_skip_upto(1048576, cs.write());
775
stack.push(std::move(cs));
780
void interpret_tlb_type_const(vm::Stack& stack, const tlb::TLB* ptr) {
781
stack.push_make_object<tlb::TlbTypeHolder>(ptr);
784
void init_words_tlb(fift::Dictionary& d) {
785
using namespace std::placeholders;
786
tlb_dict.register_types(block::gen::register_simple_types);
787
d.def_stack_word("tlb-type-lookup ", interpret_tlb_type_lookup);
788
d.def_stack_word("tlb-type-name ", interpret_tlb_type_name);
789
d.def_stack_word("tlb. ", interpret_print_tlb_type);
790
d.def_stack_word("tlb-dump-as ", interpret_tlb_dump_as);
791
d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str);
792
d.def_stack_word("tlb-skip ", interpret_tlb_skip);
793
d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip);
794
d.def_stack_word("ExtraCurrencyCollection",
795
std::bind(interpret_tlb_type_const, _1, &block::tlb::t_ExtraCurrencyCollection));
798
void usage(const char* progname) {
800
<< "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n";
802
<< "usage: " << progname
803
<< " [-i] [-n] [-I <source-include-path>] {-L <library-fif-file>} <source-file1-fif> <source-file2-fif> ...\n";
804
std::cerr << "\t-n\tDo not preload preamble files `Fift.fif` and `CreateState.fif`\n"
805
"\t-i\tForce interactive mode even if explicit source file names are indicated\n"
806
"\t-I<source-search-path>\tSets colon-separated library source include path. If not indicated, "
807
"$FIFTPATH is used instead.\n"
808
"\t-L<library-fif-file>\tPre-loads a library source file\n"
809
"\t-v<verbosity-level>\tSet verbosity level\n"
810
"\t-V<version>\tShow create-state build information\n";
814
void parse_include_path_set(std::string include_path_set, std::vector<std::string>& res) {
815
td::Parser parser(include_path_set);
816
while (!parser.empty()) {
818
auto path_separator = '@';
820
auto path_separator = ':';
822
auto path = parser.read_till_nofail(path_separator);
824
res.push_back(path.str());
826
parser.skip_nofail(path_separator);
830
void preload_preamble(fift::Fift& fift, std::string filename, bool standard = true) {
831
auto status = fift.interpret_file(filename, "");
832
if (status.is_error()) {
833
LOG(ERROR) << "Error interpreting " << (standard ? "standard" : "application-specific") << " preamble file `"
834
<< filename << "`: " << status.error().message()
835
<< "\nCheck that correct include path is set by -I or by FIFTPATH environment variable, or disable "
836
"standard preamble by -n.\n";
841
int main(int argc, char* const argv[]) {
842
td::set_default_failure_signal_handler().ensure();
843
bool interactive = false;
844
bool fift_preload = true, no_env = false, script_mode = false;
845
std::vector<std::string> library_source_files, source_list;
846
std::vector<std::string> source_include_path;
847
std::string ton_db_path;
849
fift::Fift::Config config;
852
int new_verbosity_level = VERBOSITY_NAME(INFO);
853
while (!script_mode && (i = getopt(argc, argv, "hinsI:L:v:V")) != -1) {
859
fift_preload = false;
862
LOG(ERROR) << source_include_path;
863
parse_include_path_set(optarg, source_include_path);
870
library_source_files.emplace_back(optarg);
873
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
876
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1()
877
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
885
SET_VERBOSITY_LEVEL(new_verbosity_level);
887
while (optind < argc) {
888
source_list.emplace_back(argv[optind++]);
895
const char* path = std::getenv("FIFTPATH");
897
parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path);
900
std::string current_dir;
901
auto r_current_dir = td::realpath(".");
902
if (r_current_dir.is_ok()) {
903
current_dir = r_current_dir.move_as_ok();
904
source_include_path.push_back(current_dir);
906
config.source_lookup = fift::SourceLookup(std::make_unique<fift::OsFileLoader>());
907
for (auto& path : source_include_path) {
908
config.source_lookup.add_include_path(path);
911
fift::init_words_common(config.dictionary);
912
fift::init_words_vm(config.dictionary);
913
fift::init_words_ton(config.dictionary);
914
init_words_custom(config.dictionary);
915
init_words_tlb(config.dictionary);
918
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
922
fift::Fift fift(std::move(config));
925
preload_preamble(fift, "Fift.fif", true);
926
preload_preamble(fift, "CreateState.fif", false);
929
for (auto source : library_source_files) {
930
auto status = fift.interpret_file(source, "");
931
if (status.is_error()) {
932
std::cerr << "Error interpreting preloaded file `" << source << "`: " << status.error().to_string() << std::endl;
937
if (source_list.empty() && !interactive) {
938
std::cerr << "No Fift source files specified" << std::endl;
942
for (const auto& source : source_list) {
943
auto status = fift.interpret_file(source, current_dir);
944
if (status.is_error()) {
945
std::cerr << "Error interpreting file `" << source << "`: " << status.error().to_string() << std::endl;
951
fift.interpret_istream(std::cin, current_dir).ensure();
953
// show_total_cells();