mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Limit account storage size (#530)
* Limit size of an account * Bugfix * Don't check size for special accounts * Improve timeouts in collator and validator Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
parent
a4a3ea2b77
commit
d8dd75ec83
11 changed files with 195 additions and 84 deletions
|
@ -753,6 +753,8 @@ _ MisbehaviourPunishmentConfig = ConfigParam 40;
|
|||
|
||||
size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
|
||||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig;
|
||||
size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
|
||||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32 = SizeLimitsConfig;
|
||||
_ SizeLimitsConfig = ConfigParam 43;
|
||||
|
||||
oracle_bridge_params#_ bridge_address:bits256 oracle_mutlisig_address:bits256 oracles:(HashmapE 256 uint256) external_chain_address:bits256 = OracleBridgeParams;
|
||||
|
|
|
@ -1919,16 +1919,29 @@ td::Result<SizeLimitsConfig> Config::get_size_limits_config() const {
|
|||
if (param.is_null()) {
|
||||
return limits;
|
||||
}
|
||||
gen::SizeLimitsConfig::Record rec;
|
||||
if (!tlb::unpack_cell(param, rec)) {
|
||||
auto unpack_v1 = [&](auto& rec) {
|
||||
limits.max_msg_bits = rec.max_msg_bits;
|
||||
limits.max_msg_cells = rec.max_msg_cells;
|
||||
limits.max_library_cells = rec.max_library_cells;
|
||||
limits.max_vm_data_depth = static_cast<td::uint16>(rec.max_vm_data_depth);
|
||||
limits.ext_msg_limits.max_size = rec.max_ext_msg_size;
|
||||
limits.ext_msg_limits.max_depth = static_cast<td::uint16>(rec.max_ext_msg_depth);
|
||||
};
|
||||
|
||||
auto unpack_v2 = [&](auto& rec) {
|
||||
unpack_v1(rec);
|
||||
limits.max_acc_state_bits = rec.max_acc_state_bits;
|
||||
limits.max_acc_state_cells = rec.max_acc_state_cells;
|
||||
};
|
||||
gen::SizeLimitsConfig::Record_size_limits_config rec_v1;
|
||||
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2;
|
||||
if (tlb::unpack_cell(param, rec_v1)) {
|
||||
unpack_v1(rec_v1);
|
||||
} else if (tlb::unpack_cell(param, rec_v2)) {
|
||||
unpack_v2(rec_v2);
|
||||
} else {
|
||||
return td::Status::Error("configuration parameter 43 is invalid");
|
||||
}
|
||||
limits.max_msg_bits = rec.max_msg_bits;
|
||||
limits.max_msg_cells = rec.max_msg_cells;
|
||||
limits.max_library_cells = rec.max_library_cells;
|
||||
limits.max_vm_data_depth = static_cast<td::uint16>(rec.max_vm_data_depth);
|
||||
limits.ext_msg_limits.max_size = rec.max_ext_msg_size;
|
||||
limits.ext_msg_limits.max_depth = static_cast<td::uint16>(rec.max_ext_msg_depth);
|
||||
return limits;
|
||||
}
|
||||
|
||||
|
|
|
@ -387,6 +387,8 @@ struct SizeLimitsConfig {
|
|||
td::uint32 max_library_cells = 1000;
|
||||
td::uint16 max_vm_data_depth = 512;
|
||||
ExtMsgLimits ext_msg_limits;
|
||||
td::uint32 max_acc_state_cells = 1 << 16;
|
||||
td::uint32 max_acc_state_bits = (1 << 16) * 1023;
|
||||
};
|
||||
|
||||
struct CatchainValidatorsConfig {
|
||||
|
|
|
@ -460,7 +460,6 @@ bool Account::deactivate() {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Account::belongs_to_shard(ton::ShardIdFull shard) const {
|
||||
return workchain == shard.workchain && ton::shard_is_ancestor(shard.shard, addr);
|
||||
}
|
||||
|
@ -1126,6 +1125,25 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
ap.total_action_fees = td::zero_refint();
|
||||
ap.reserved_balance.set_zero();
|
||||
|
||||
td::Ref<vm::Cell> old_code = new_code, old_data = new_data, old_library = new_library;
|
||||
auto enforce_state_size_limits = [&]() {
|
||||
if (account.is_special) {
|
||||
return true;
|
||||
}
|
||||
if (!check_state_size_limit(cfg)) {
|
||||
// Rollback changes to state, fail action phase
|
||||
LOG(INFO) << "Account state size exceeded limits";
|
||||
new_storage_stat.clear();
|
||||
new_code = old_code;
|
||||
new_data = old_data;
|
||||
new_library = old_library;
|
||||
ap.result_code = 50;
|
||||
ap.state_size_too_big = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
int n = 0;
|
||||
while (true) {
|
||||
ap.action_list.push_back(list);
|
||||
|
@ -1201,9 +1219,21 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
ap.no_funds = true;
|
||||
}
|
||||
LOG(DEBUG) << "invalid action " << ap.result_arg << " in action list: error code " << ap.result_code;
|
||||
// This is reuqired here because changes to libraries are applied even if actipn phase fails
|
||||
enforce_state_size_limits();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
end_lt = ap.end_lt;
|
||||
if (ap.new_code.not_null()) {
|
||||
new_code = ap.new_code;
|
||||
}
|
||||
new_data = compute_phase->new_data; // tentative persistent data update applied
|
||||
if (!enforce_state_size_limits()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ap.result_arg = 0;
|
||||
ap.result_code = 0;
|
||||
CHECK(ap.remaining_balance.grams->sgn() >= 0);
|
||||
|
@ -1217,12 +1247,7 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
was_deleted = true;
|
||||
}
|
||||
ap.success = true;
|
||||
end_lt = ap.end_lt;
|
||||
out_msgs = std::move(ap.out_msgs);
|
||||
if (ap.new_code.not_null()) {
|
||||
new_code = ap.new_code;
|
||||
}
|
||||
new_data = compute_phase->new_data; // tentative persistent data update applied
|
||||
total_fees +=
|
||||
ap.total_action_fees; // NB: forwarding fees are not accounted here (they are not collected by the validators in this transaction)
|
||||
balance = ap.remaining_balance;
|
||||
|
@ -1811,6 +1836,35 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap,
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool Transaction::check_state_size_limit(const ActionPhaseConfig& cfg) {
|
||||
auto cell_equal = [](const td::Ref<vm::Cell>& a, const td::Ref<vm::Cell>& b) -> bool {
|
||||
if (a.is_null()) {
|
||||
return b.is_null();
|
||||
}
|
||||
if (b.is_null()) {
|
||||
return false;
|
||||
}
|
||||
return a->get_hash() == b->get_hash();
|
||||
};
|
||||
if (cell_equal(account.code, new_code) && cell_equal(account.data, new_data) &&
|
||||
cell_equal(account.library, new_library)) {
|
||||
return true;
|
||||
}
|
||||
// new_storage_stat is used here beause these stats will be reused in compute_state()
|
||||
new_storage_stat.limit_cells = cfg.size_limits.max_acc_state_cells;
|
||||
new_storage_stat.limit_bits = cfg.size_limits.max_acc_state_bits;
|
||||
new_storage_stat.add_used_storage(new_code);
|
||||
new_storage_stat.add_used_storage(new_data);
|
||||
new_storage_stat.add_used_storage(new_library);
|
||||
if (acc_status == Account::acc_active) {
|
||||
new_storage_stat.clear_limit();
|
||||
} else {
|
||||
new_storage_stat.clear();
|
||||
}
|
||||
return new_storage_stat.cells <= cfg.size_limits.max_acc_state_cells &&
|
||||
new_storage_stat.bits <= cfg.size_limits.max_acc_state_bits;
|
||||
}
|
||||
|
||||
bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
|
||||
if (in_msg.is_null() || !bounce_enabled) {
|
||||
return false;
|
||||
|
@ -2035,7 +2089,7 @@ bool Transaction::compute_state() {
|
|||
stats = new_stats.unwrap();
|
||||
} else {
|
||||
td::Timer timer;
|
||||
CHECK(stats.compute_used_storage(Ref<vm::Cell>(storage)));
|
||||
CHECK(stats.add_used_storage(Ref<vm::Cell>(storage)));
|
||||
if (timer.elapsed() > 0.1) {
|
||||
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
|
||||
}
|
||||
|
|
|
@ -184,6 +184,7 @@ struct ActionPhase {
|
|||
bool code_changed{false};
|
||||
bool action_list_invalid{false};
|
||||
bool acc_delete_req{false};
|
||||
bool state_size_too_big{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
|
||||
|
@ -351,6 +352,7 @@ struct Transaction {
|
|||
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 check_state_size_limit(const ActionPhaseConfig& cfg);
|
||||
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
|
||||
bool compute_state();
|
||||
bool serialize();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue