Skip to content

Commit

Permalink
Fixed finalize() fallback logic
Browse files Browse the repository at this point in the history
Fixed an issue in finalize() logic when it checks contracts resources.
Logic was incorrect for measuring accounts with unlimited CPU / NET.
  • Loading branch information
dtaghavi committed Dec 13, 2024
1 parent ec45733 commit 8147efd
Showing 1 changed file with 30 additions and 19 deletions.
49 changes: 30 additions & 19 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,11 @@ namespace sysio { namespace chain {

uint32_t greylist_limit = (greylisted_net || greylisted_cpu) ? 1 : config::maximum_elastic_resource_multiplier;

// Possibly lower net_limit to what the billed accounts can pay
if (static_cast<uint64_t>(account_net_limit) <= net_limit) {
net_limit = static_cast<uint64_t>(account_net_limit);
net_limit_due_to_block = false;
}

// Possibly lower objective_duration_limit to what the billed accounts can pay
if (account_cpu_limit <= objective_duration_limit.count()) {
objective_duration_limit = fc::microseconds(account_cpu_limit);
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
Expand Down Expand Up @@ -393,13 +391,11 @@ namespace sysio { namespace chain {
}
}

// Helper lambda to check if an account is considered a "system" account
auto is_system_account = [&](const account_name& acct) {
std::string acct_str = acct.to_string();
return (acct == config::system_account_name || acct_str.rfind("sysio.", 0) == 0);
};

// Determine if we are still in a BIOS scenario by checking if all billed accounts are system-level
bool only_system_accounts = true;
for (const auto& acct : bill_to_accounts) {
if (!is_system_account(acct)) {
Expand All @@ -408,48 +404,64 @@ namespace sysio { namespace chain {
}
}

// If only system accounts, consider it BIOS (or system initialization) scenario and skip fallback logic
if (only_system_accounts) {
// All involved accounts are system or sysio.* accounts
// This implies we are still in the BIOS boot or system initialization phase
// Skip the new fallback billing logic and revert to original behavior
return;
}

// If we reach this point, we assume BIOS boot is complete and apply fallback logic
// Past BIOS boot, apply fallback logic
SYS_ASSERT(user_account.good(), transaction_exception, "No user account found among bill_to_accounts");

// Get contract resource limits
auto [contract_cpu, contract_cpu_grey] = rl.get_account_cpu_limit_ex(contract_account, greylist_limit);
auto [contract_net, contract_net_grey] = rl.get_account_net_limit_ex(contract_account, greylist_limit);

int64_t total_ram_used = total_ram_usage;
int64_t c_ram_limit, c_net_w, c_cpu_w;
rl.get_account_limits(contract_account, c_ram_limit, c_net_w, c_cpu_w);
int64_t c_ram_usage = rl.get_account_ram_usage(contract_account);
int64_t c_ram_available = (c_ram_limit < 0) ? std::numeric_limits<int64_t>::max() : (c_ram_limit - c_ram_usage);
int64_t c_ram_available = (c_ram_limit < 0) ? -1 : (c_ram_limit - c_ram_usage);
// Note: If c_ram_limit < 0, it's just a negative limit (not unlimited), handle as insufficient if it doesn't cover usage.

bool contract_can_pay = (contract_cpu.available >= billed_cpu_time_us)
&& (contract_net.available >= (int64_t)net_usage)
&& (c_ram_available >= total_ram_used);
// Handle unlimited resources for CPU/NET only
bool contract_cpu_unlimited = (contract_cpu.available < 0);
bool contract_net_unlimited = (contract_net.available < 0);

bool contract_can_pay_cpu = contract_cpu_unlimited || (contract_cpu.available >= billed_cpu_time_us);
bool contract_can_pay_net = contract_net_unlimited || (contract_net.available >= (int64_t)net_usage);

// RAM: no unlimited concept. Just check c_ram_available.
// If c_ram_limit < 0 or c_ram_available < total_ram_used, then insufficient.
// Negative c_ram_limit or zero doesn't mean unlimited; it means no or insufficient quota.
bool contract_can_pay_ram = (c_ram_available >= total_ram_used);

bool contract_can_pay = contract_can_pay_cpu && contract_can_pay_net && contract_can_pay_ram;

if (contract_can_pay) {
// Charge the contract
// Charge contract
rl.add_pending_ram_usage(contract_account, total_ram_used);
rl.verify_account_ram_usage(contract_account);
rl.add_transaction_usage({contract_account}, static_cast<uint64_t>(billed_cpu_time_us), net_usage,
block_timestamp_type(control.pending_block_time()).slot );
} else {
// Try user as fallback
// Get user resource limits
auto [user_cpu, user_cpu_grey] = rl.get_account_cpu_limit_ex(user_account, greylist_limit);
auto [user_net, user_net_grey] = rl.get_account_net_limit_ex(user_account, greylist_limit);

int64_t u_ram_limit, u_net_w, u_cpu_w;
rl.get_account_limits(user_account, u_ram_limit, u_net_w, u_cpu_w);
int64_t u_ram_usage = rl.get_account_ram_usage(user_account);
int64_t u_ram_available = (u_ram_limit < 0) ? std::numeric_limits<int64_t>::max() : (u_ram_limit - u_ram_usage);
int64_t u_ram_available = (u_ram_limit < 0) ? -1 : (u_ram_limit - u_ram_usage);

bool user_cpu_unlimited = (user_cpu.available < 0);
bool user_net_unlimited = (user_net.available < 0);

bool user_can_pay = (user_cpu.available >= billed_cpu_time_us)
&& (user_net.available >= (int64_t)net_usage)
&& (u_ram_available >= total_ram_used);
bool user_can_pay_cpu = user_cpu_unlimited || (user_cpu.available >= billed_cpu_time_us);
bool user_can_pay_net = user_net_unlimited || (user_net.available >= (int64_t)net_usage);

bool user_can_pay_ram = (u_ram_available >= total_ram_used);

bool user_can_pay = user_can_pay_cpu && user_can_pay_net && user_can_pay_ram;

SYS_ASSERT(user_can_pay, resource_exhausted_exception, "Neither contract nor user can pay for this transaction");

Expand All @@ -462,7 +474,6 @@ namespace sysio { namespace chain {
}



void transaction_context::squash() {
if (undo_session) undo_session->squash();
}
Expand Down

0 comments on commit 8147efd

Please sign in to comment.