Skip to content

Commit 7262a66

Browse files
Don't allow deploying a contract with public libs (ton-blockchain#812)
* Check account size limits in unpack_msg_state * Don't allow deploying a contract with public libs --------- Co-authored-by: SpyCheese <[email protected]>
1 parent d9580ea commit 7262a66

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed

crypto/block/transaction.cpp

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,11 +1290,13 @@ int output_actions_count(Ref<vm::Cell> list) {
12901290
/**
12911291
* Unpacks the message StateInit.
12921292
*
1293+
* @param cfg The configuration for the compute phase.
12931294
* @param lib_only If true, only unpack libraries from the state.
1295+
* @param forbid_public_libs Don't allow public libraries in initstate.
12941296
*
12951297
* @returns True if the unpacking is successful, false otherwise.
12961298
*/
1297-
bool Transaction::unpack_msg_state(bool lib_only) {
1299+
bool Transaction::unpack_msg_state(const ComputePhaseConfig& cfg, bool lib_only, bool forbid_public_libs) {
12981300
block::gen::StateInit::Record state;
12991301
if (in_msg_state.is_null() || !tlb::unpack_cell(in_msg_state, state)) {
13001302
LOG(ERROR) << "cannot unpack StateInit from an inbound message";
@@ -1318,9 +1320,22 @@ bool Transaction::unpack_msg_state(bool lib_only) {
13181320
new_tock = z & 1;
13191321
LOG(DEBUG) << "tick=" << new_tick << ", tock=" << new_tock;
13201322
}
1323+
td::Ref<vm::Cell> old_code = new_code, old_data = new_data, old_library = new_library;
13211324
new_code = state.code->prefetch_ref();
13221325
new_data = state.data->prefetch_ref();
13231326
new_library = state.library->prefetch_ref();
1327+
auto size_limits = cfg.size_limits;
1328+
if (forbid_public_libs) {
1329+
size_limits.max_acc_public_libraries = 0;
1330+
}
1331+
auto S = check_state_limits(size_limits, false);
1332+
if (S.is_error()) {
1333+
LOG(DEBUG) << "Cannot unpack msg state: " << S.move_as_error();
1334+
new_code = old_code;
1335+
new_data = old_data;
1336+
new_library = old_library;
1337+
return false;
1338+
}
13241339
return true;
13251340
}
13261341

@@ -1408,7 +1423,9 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
14081423
return true;
14091424
}
14101425
use_msg_state = true;
1411-
if (!(unpack_msg_state() && account.check_split_depth(new_split_depth))) {
1426+
bool forbid_public_libs =
1427+
acc_status == Account::acc_uninit && account.is_masterchain(); // Forbid for deploying, allow for unfreezing
1428+
if (!(unpack_msg_state(cfg, false, forbid_public_libs) && account.check_split_depth(new_split_depth))) {
14121429
LOG(DEBUG) << "cannot unpack in_msg_state, or it has bad split_depth; cannot init account state";
14131430
cp.skip_reason = ComputePhase::sk_bad_state;
14141431
return true;
@@ -1423,7 +1440,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
14231440
cp.skip_reason = in_msg_state.not_null() ? ComputePhase::sk_bad_state : ComputePhase::sk_no_state;
14241441
return true;
14251442
} else if (in_msg_state.not_null()) {
1426-
unpack_msg_state(true); // use only libraries
1443+
unpack_msg_state(cfg, true); // use only libraries
14271444
}
14281445
if (in_msg_extern && in_msg_state.not_null() && account.addr != in_msg_state->get_hash().bits()) {
14291446
LOG(DEBUG) << "in_msg_state hash mismatch in external message";
@@ -1560,7 +1577,7 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
15601577
if (account.is_special) {
15611578
return true;
15621579
}
1563-
auto S = check_state_limits(cfg);
1580+
auto S = check_state_limits(cfg.size_limits);
15641581
if (S.is_error()) {
15651582
// Rollback changes to state, fail action phase
15661583
LOG(INFO) << "Account state size exceeded limits: " << S.move_as_error();
@@ -2523,13 +2540,14 @@ static td::uint32 get_public_libraries_count(const td::Ref<vm::Cell>& libraries)
25232540
* Checks that the new account state fits in the limits.
25242541
* This function is not called for special accounts.
25252542
*
2526-
* @param cfg The configuration for the action phase.
2543+
* @param size_limits The size limits configuration.
2544+
* @param update_storage_stat Store storage stat in the Transaction's CellStorageStat.
25272545
*
25282546
* @returns A `td::Status` indicating the result of the check.
25292547
* - If the state limits are within the allowed range, returns OK.
25302548
* - If the state limits exceed the maximum allowed range, returns an error.
25312549
*/
2532-
td::Status Transaction::check_state_limits(const ActionPhaseConfig& cfg) {
2550+
td::Status Transaction::check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat) {
25332551
auto cell_equal = [](const td::Ref<vm::Cell>& a, const td::Ref<vm::Cell>& b) -> bool {
25342552
if (a.is_null()) {
25352553
return b.is_null();
@@ -2543,13 +2561,13 @@ td::Status Transaction::check_state_limits(const ActionPhaseConfig& cfg) {
25432561
cell_equal(account.library, new_library)) {
25442562
return td::Status::OK();
25452563
}
2546-
// new_storage_stat is used here beause these stats will be reused in compute_state()
2547-
new_storage_stat.limit_cells = cfg.size_limits.max_acc_state_cells;
2548-
new_storage_stat.limit_bits = cfg.size_limits.max_acc_state_bits;
2564+
vm::CellStorageStat storage_stat;
2565+
storage_stat.limit_cells = size_limits.max_acc_state_cells;
2566+
storage_stat.limit_bits = size_limits.max_acc_state_bits;
25492567
td::Timer timer;
25502568
auto add_used_storage = [&](const td::Ref<vm::Cell>& cell) -> td::Status {
25512569
if (cell.not_null()) {
2552-
TRY_RESULT(res, new_storage_stat.add_used_storage(cell));
2570+
TRY_RESULT(res, storage_stat.add_used_storage(cell));
25532571
if (res.max_merkle_depth > max_allowed_merkle_depth) {
25542572
return td::Status::Error("too big merkle depth");
25552573
}
@@ -2563,19 +2581,24 @@ td::Status Transaction::check_state_limits(const ActionPhaseConfig& cfg) {
25632581
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
25642582
}
25652583
if (acc_status == Account::acc_active) {
2566-
new_storage_stat.clear_limit();
2584+
storage_stat.clear_limit();
25672585
} else {
2568-
new_storage_stat.clear();
2569-
}
2570-
if (new_storage_stat.cells > cfg.size_limits.max_acc_state_cells ||
2571-
new_storage_stat.bits > cfg.size_limits.max_acc_state_bits) {
2572-
return td::Status::Error("account state is too big");
2586+
storage_stat.clear();
2587+
}
2588+
td::Status res;
2589+
if (storage_stat.cells > size_limits.max_acc_state_cells || storage_stat.bits > size_limits.max_acc_state_bits) {
2590+
res = td::Status::Error(PSTRING() << "account state is too big");
2591+
} else if (account.is_masterchain() && !cell_equal(account.library, new_library) &&
2592+
get_public_libraries_count(new_library) > size_limits.max_acc_public_libraries) {
2593+
res = td::Status::Error("too many public libraries");
2594+
} else {
2595+
res = td::Status::OK();
25732596
}
2574-
if (account.is_masterchain() && !cell_equal(account.library, new_library) &&
2575-
get_public_libraries_count(new_library) > cfg.size_limits.max_acc_public_libraries) {
2576-
return td::Status::Error("too many public libraries");
2597+
if (update_storage_stat) {
2598+
// storage_stat will be reused in compute_state()
2599+
new_storage_stat = std::move(storage_stat);
25772600
}
2578-
return td::Status::OK();
2601+
return res;
25792602
}
25802603

25812604
/**
@@ -3407,6 +3430,7 @@ td::Status FetchConfigParams::fetch_config_params(
34073430
compute_phase_cfg->prev_blocks_info = std::move(prev_blocks_info);
34083431
}
34093432
compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
3433+
compute_phase_cfg->size_limits = size_limits;
34103434
}
34113435
{
34123436
// compute action_phase_cfg

crypto/block/transaction.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct ComputePhaseConfig {
116116
int global_version = 0;
117117
Ref<vm::Tuple> prev_blocks_info;
118118
std::unique_ptr<vm::Dictionary> suspended_addresses;
119+
SizeLimitsConfig size_limits;
119120
int vm_log_verbosity = 0;
120121

121122
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
@@ -372,7 +373,7 @@ struct Transaction {
372373
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
373374
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
374375
bool prepare_action_phase(const ActionPhaseConfig& cfg);
375-
td::Status check_state_limits(const ActionPhaseConfig& cfg);
376+
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
376377
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
377378
bool compute_state();
378379
bool serialize();
@@ -404,7 +405,7 @@ struct Transaction {
404405
bool serialize_compute_phase(vm::CellBuilder& cb);
405406
bool serialize_action_phase(vm::CellBuilder& cb);
406407
bool serialize_bounce_phase(vm::CellBuilder& cb);
407-
bool unpack_msg_state(bool lib_only = false);
408+
bool unpack_msg_state(const ComputePhaseConfig& cfg, bool lib_only = false, bool forbid_public_libs = false);
408409
};
409410
} // namespace transaction
410411

validator/impl/validate-query.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ bool ValidateQuery::fetch_config_params() {
954954
compute_phase_cfg_.prev_blocks_info = prev_blocks_info.move_as_ok();
955955
}
956956
compute_phase_cfg_.suspended_addresses = config_->get_suspended_addresses(now_);
957+
compute_phase_cfg_.size_limits = size_limits;
957958
}
958959
{
959960
// compute action_phase_cfg

0 commit comments

Comments
 (0)