Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic energy model in TRON protocol #491

Closed
daniel-cao-byte opened this issue Dec 19, 2022 · 11 comments
Closed

Dynamic energy model in TRON protocol #491

daniel-cao-byte opened this issue Dec 19, 2022 · 11 comments

Comments

@daniel-cao-byte
Copy link
Contributor

daniel-cao-byte commented Dec 19, 2022

tip: 491
title: Dynamic energy model in TRON protocol
author: daniel.cao@tron.network
discussions-to: https://github.com/tronprotocol/tips/issues/491
status: Final
type: Standards Track
category: Core
created: 2022-12-19

Simple Summary

This tip proposes a mechanism to perform dynamic regulation of energy in contracts to balance the distribution of energy among contracts.

Abstract

In this tip, we propose a mechanism to dynamically adjust the energy consumption of a contract according to its execution resource usage.

If a contract uses too much CPU resources in one cycle, the energy consumption of that contract will add a percentage as penalty in the next cycle; and when its use of resources is reasonable, the energy consumption of that contract will gradually return to normal. Through this mechanism, we hope to make the distribution of energy resources on the chain more reasonable, and combat low-value or fraudulent transactions while allowing more projects and contracts to have a chance for development.

Motivation

According to Tronscan, more than 85% of the current TRON network's CPU execution time is concentrated on a few contracts. And some of the transactions are low-value or even fraudulent ones.

If this situation persists,

  • It will not only lead to more users being defrauded to suffer asset loss;
  • It will also make it difficult for the dApp ecosystem to grow. The capacity of the network is limited, and when the network is filled with a large number of queued spam transactions, other transactions will have to wait longer, which will be a very bad experience for both the users and the dApp developers.

Therefore, we propose to introduce a dynamic energy model to increase the transaction cost of low-value and fraudulent transactions without affecting other dApps, and also to guarantee the robustness, diversity and balanced development of the ecosystem.

Specification

Three main parameters controlled by the proposal are introduced in the scheme, and the meaning of each parameter is as follows,

  • threshold: threshold value of base energy consumption for the contract hit rule, then the energy consumption factor will be adjusted.
  • increase_factor: the rate of the consumption factor increase when the base energy consumption of the contract exceeds the threshold
  • max_factor: the maximum value of the energy consumption factor

And there is a derivative parameter for calculation:

  • decrease_factor: a quarter of increase_factor, the rate of the consumption factor decrease when the base energy consumption of the contract does not exceed the threshold

The scheme mechanism is as follows.

During each maintenance period, the base energy consumed by the contract execution is recorded. If the contract's base energy consumption exceeds threshold during a maintenance period, then its consumption_factor (1 + factor) will be increased by increase_factor during the next maintenance period, the part that exceeds 1 is the penalty factor of the contract.

Otherwise, it will be scaled down by a decrease_factor until consumption_factor grows to max_factor+1 or drops back to 1. Each contract's instructions will be scaled up by consumption_factor when calculating energy consumption.

When threshold is exceeded in the previous maintenance period:

consumption_factor = min(consumption_factor * (1 + increaese_factor), max_factor+1)

consumption_factor = 1 + factor

factor = min((1 + factor) * (1 + increaese_factor) - 1, max_factor)

Otherwise,

decrease_factor = increaese_factor / 4

consumption_factor = max(consumption_factor * (1 - decrease_factor), 1)

factor = max((1 + factor) * (1 - decrease_factor) - 1, 0)

The energy consumption of a trigger_smart_contract will be:

# assume a trigger involving n contracts, 
# for each contract i,
cost_origin_i = sum(cost_instruction)
cost_penality_i = sum(cost_instruction * factor_i)
cost_i = cost_origin_i + cost_penality_i

# for the entire trigger
cost = sum(cost_1, cost_2, ..., cost_n)

Rationale

Cycle selection

We use a fixed cycle because dynamic cycle selection introduces additional complexity for the chain, and its increased flexibility can basically be supplemented by dynamic adjustment of the threshold. Too short a period tends to make some temporary popular contracts hit the rules and interfere with various dApps to carry out innovative activities; at the same time, the length of the period is best to have a record of the corresponding concept on the chain, reducing unnecessary development and maintenance costs. For all these reasons, we suggest the length of the maintenance period as the statistical cycle of this program.

Effective method

We choose a gradual scaling approach, and when the contract consumes base energy continuously exceeding the threshold, its scaling factor will also be continuously scaled up, which draws on the idea of dynamic adjustment of other mainstream public chains' resources by usage. At the same time, we add the limit of the maximum amplification factor in order to avoid infinite amplification.

Adjustment method

We introduce a factorized scaling of the energy consumed by the execution of a contract instruction. The method correlates with the actual execution of the contract.

Backward Compatibility

There are no backward compatibility issues.

API Changes

There are several API changes involved in this TIP.

  1. Add the contract_state structure in /wallet/getcontractinfo
{
  "runtimecode": "",
  "smart_contract": {},
  "contract_state": {
    "energy_usage": 2000,
    "energy_factor": 10000,
    "update_cycle": 500
  }
}

energy_usage: the origin energy consumption of the contract in current maintenance period.
energy_factor: the penalty factor of the contract in current maintenance period, the precision is 10_000, energy_factor = 1000 means the penalty percentage is 10%.
update_cycle: the current maintenance period number.

  1. Add energy_penalty in contract trigger results, involving /wallet/triggerconstantcontract and triggersmartcontract
{
    "...": "...",
    "energy_used": 643,
    "energy_penalty": 200
}

energy_penalty: the total penalty energy in the transaction.

  1. Add energy_penalty_total in transaction receipts, involving /wallet/gettransactioninfobyid and /wallet/gettransactionreceiptbyid

/wallet/gettransactioninfobyid

{
    "id": "",
    "blockNumber":,
    "...": "...",
    "receipt":
    {
        "energy_usage": 98115,
        "energy_usage_total": 98115,
        "net_usage": 379,
        "result": "SUCCESS",
        "energy_penalty_total": 5000
    }
}

receipt.energy_penalty_total: the total penalty energy in the transaction.

/wallet/gettransactionreceiptbyid

{
    "Receipt": {
        "result": "SUCCESS",
        "energy_fee": 618400,
        "energy_usage_total": 6184,
        "net_usage": 313,
        "energy_penalty_total": 719
    }
}

Receipt.energy_penalty_total: the total penalty energy in the transaction.

Security Considerations

There are no security considerations.

@otakuinny
Copy link

threshold: threshold value of base energy consumption for the contract hit rule

@daniel-cao-byte, no idea what you mean by above. Can you use an example with numbers so we can understand.
Also, you do know that the phishing scammers just call the USDT contract directly, right? In this case, the transaction cost of the USDT contract will rise which leads to more expensive usdt transfer costs for everybody. Unless, I misunderstood your proposal.

@daniel-cao-byte
Copy link
Contributor Author

daniel-cao-byte commented Dec 19, 2022

@otakuinny Thanks for your comment. Here is an example, I believe it can help indicate how the mechanism works.

Let's assume the parameters are as below:

  • threshold: 5_000_000_000
  • increase_factor: 0.5
  • max_factor: 1

and decrease_factor is 1/4 the range of the increase.

  • decrease_factor: increase_factor / 4 = 0.5 / 4 = 0.125

And there exist two kinds of contract calls.

  • Contract_A.a_call(), its instructions cost energy 10_000, origin_A
  • Contract_B.b_call(), calls Contract_A.a_call() within the function, total cost energy 30_000, including origin_A 10_000 and origin_B 20_000.

Maintenance N+1

If Contract_A consumes more than threshold energy in the maintenance period N, then in the next maintenance period N+1, the amplification factor of each contract are:

  • factor_A: (1 + prev_factor_A) * (1 + increase_factor) - 1 = 1 * 1.5 - 1 = 0.5
  • factor_B: MAX(0, (1 + prev_factor_B) * (1 - decrease_factor) - 1) = MAX(0, 1 * 0.875 - 1) = MAX(0, -0.125) = 0

Direct trigger to each function will cost:

  • Contract_A: a_call(), will cost: origin_A + origin_A * factor_A = 10_000 + 10_000 * 0.5 = 15_000
  • Contract_B: b_call(), will cost: origin_B + origin_B * factor_B + origin_A + origin_A * factor_A = 20_000 + 20_000 * 0 + 10_000 + 10_000 * 0.5 = 35_000

Maintenance N+2

If Contract_A continues to consume more than threshold energy in the maintenance period N+1, then in the N+2 maintenance period:

  • factor_A: MIN(max_factor, (1 + prev_factor_A) * (1 + increase_factor)-1) = MIN(1, 1.5 * 1.5 - 1) = MIN(1, 1.25) = 1

  • factor_B: MAX(0, (1 + prev_factor_B) * (1 - decrease_factor) - 1) = MAX(0, 1 * 0.875 - 1) = 0

  • Contract_A: a_call(), will cost: origin_A + origin_A * factor_A = 10_000 + 10_000 * 1 = 20_000

  • Contract_B: b_call(), will cost: origin_B + origin_B * factor_B + origin_A + origin_A * factor_A = 20_000 + 20_000 * 0 + 10_000 + 10_000 * 1 = 40_000

Maintenance N+3

If Contract_A consumes less than threshold in the N+2 maintenance period, but Contract_B consumes more than threshold`, then in the N+3 period:

  • factor_A: MAX(0, (1 + prev_factor_A) * (1 - decrease_factor) - 1) = MAX(0, 2 * 0.875 - 1) = 0.75

  • factor_B: MIN(max_factor, (1 + prev_factor_B) * (1 + increase_factor) - 1) = MIN(1, 1 * 1.5 - 1) = MIN(1, 0.5) = 0.5

  • Contract_A: a_call(), will cost: origin_A + origin_A * factor_A = 10_000 + 10_000 * 0.75 = 17_500

  • Contract_B: b_call(), will cost: origin_B + origin_B * factor_B + origin_A + origin_A * factor_A = 20_000 + 20_000 * 0.5 + 10_000 + 10_000 * 0.75 = 47_500

@otakuinny
Copy link

@otakuinny Thanks for your comment. Here is an example, I believe it can help indicate how the mechanism works.

Let's assume the parameters are as below:

  • threshold: 5_000_000_000
  • increase_factor: 1.5
  • max_factor: 2
  • trigger_base_energy: 2_000

and decrease_factor is 1/4 the range of the increase.

  • decrease_factor: 1 - (increase_factor - 1) / 4 = 1 - (1.5 - 1) / 4 = 0.875

And there exist two kinds of contract calls.

  • Contract_A.a_call(), its instructions cost energy 10_000, origin_A
  • Contract_B.b_call(), calls Contract_A.a_call() within the function, total cost energy 30_000, including origin_A 10_000 and origin_B 20_000.

Maintenance N+1

If Contract_A consumes more than threshold energy in the maintenance period N, then in the next maintenance period N+1, the amplification factor of each contract are:

  • factor_A: prev_factor_A * increase_factor = 1 * 1.5 = 1.5
  • factor_B: MAX(1, prev_factor_B * decrease_factor) = MAX(1, 1 * 0.875) = 1

Direct trigger to each function will cost:

  • Contract_A: a_call(), will cost: origin_A * factor_A + trigger_base_energy * factor_A = 10_000 * 1.5 + 2_000 * 1.5 = 18_000
  • Contract_B: b_call(), will cost: origin_B * factor_B + origin_A * factor_A = 20_000 * 1 + 10_000 * 1.5 = 35_000

Maintenance N+2

If Contract_A continues to consume more than threshold energy in the maintenance period N+1, then in the N+2 maintenance period:

  • factor_A: MIN(max_factor, prev_factor_A * increase_factor) = MIN(2, 1.5 * 1.5) = 2
  • factor_B: MAX(1, prev_factor_B * decrease_factor) = MAX(1, 1 * 0.875) = 1
  • Contract_A: a_call(), will cost: origin_A * factor_A + trigger_base_energy * factor_A = 10_000 * 2 + 2_000 * 2 = 24_000
  • Contract_B: b_call(), will cost: origin_B * factor_B + origin_A * factor_A = 20_000 * 1 + 10_000 * 2 = 40_000

Maintenance N+3

If Contract_A consumes less than threshold in the N+2 maintenance period, but Contract_B consumes more than threshold`, then in the N+3 period:

  • factor_A: MAX(1, prev_factor_A * decrease_factor) = MAX(1, 2 * 0.875) = 1.75
  • factor_B: MIN(max_factor, prev_factor_B * increase_factor) = MIN(2, 1 * 1.5) = 1.5
  • Contract_A: a_call(), will cost: origin_A * factor_A + trigger_base_energy * factor_A = 10_000 * 1.75 + 2_000 * 1.75 = 21_000
  • Contract_B: b_call(), will cost: origin_B * factor_B + origin_A * factor_A + trigger_base_energy * factor_B = 20_000 * 1.5 + 10_000 * 1.75 + 2_000 * 1.5 = 50_500

Thank you @daniel-cao-byte for taking the time to write-up the detailed example with numbers.
The tron resource known as energy is a measure of how long it takes for cpu to execute instructions in VM. 1 energy point = 1 micro second. So, if a call takes 100 micro seconds to finish, the call consumes 100 energy points. This being the case, dynamically raising the energy consumed by a contract will not make much sense as the call will always take the same amount of time assuming the input parameters and the storage data does not change.

Besides, the scam contracts that make millions of transactions can always rent more energy at a cheaper price even if the energy consumption is raised by a factor. A better system could be to charge TRX fees (just like 1 TRX for memo) when a contract exceeds a threshold in a cycle. This way we don't have to worry about complex calculations and max factors etc..

E.g. We can make the TRX fees configurable using SR proposals. Let's say it is 5 TRX and the threshold is 5 billion energy.
If a contract uses more than 5 billion energy in a given cycle, write calls to that contract will incur a fee of 5 TRX in the next cycle. This fees can only be paid in TRX. When the contract consumes less than the threshold energy, the fee is not charged in the next cycle. I think this is much more effective and may even help reduce TRX circulation.

So, to recap, charging trx fees instead of charging more energy might be the way to go as the contract can provide 100% of the energy by renting it cheaply.

But we should be careful about setting the threshold value and the TRX fees so as to not hurt legitimate contracts. Also, when the contract call costs a trx fees, there should be a way for the caller to know ahead of time so a message can be shown on the dapp saying it'll cost the caller trx.

Just my 2 cents.

@daniel-cao-byte
Copy link
Contributor Author

I think it's a very good advice to charge TRX fee instead of energy, we will carefully discuss and evaluate the suggestion.

@daniel-cao-byte
Copy link
Contributor Author

After our discussion, we think that adding a percentage of penalty energy is more reasonable than charging a fixed TRX fee for whatever the transaction is or how much energy it consumes. Also, charging for trigger to a specific contract can be easily bypassed by a 'bridge' contract.

@dwjorgeb
Copy link

so, I just came here from the release notes of v4.7.0, and after I read the proposal I have come with the same conclusion as @otakuinny

Also, you do know that the phishing scammers just call the USDT contract directly, right? In this case, the transaction cost of the USDT contract will rise which leads to more expensive usdt transfer costs for everybody. Unless, I misunderstood your proposal.

Basically what this does is to turn USDT transfers more expensive for everyone, right?

@daniel-cao-byte
Copy link
Contributor Author

This might be one of the TIP's side effect, but not only USDT, any contracts consuming too much energy will consume extra penalty energy.

@dwjorgeb
Copy link

This might be one of the TIP's side effect, but not only USDT, any contracts consuming too much energy will consume extra penalty energy.

USDT is 90% of the energy consumed on TRON (link), this TIP will just help turning TRON into Ethereum, where USDT payments for small value items (< 10 USDT) are unbearable pay via the TRON network, which will just push people to stop using TRON.

So, basically, this TIP is kneecapping one of the biggest uses of the TRON network without providing any benefits whatsoever to anyone else, nor any meaningful spam prevention system.

How was this TIP approved?

@otakuinny
Copy link

This might be one of the TIP's side effect, but not only USDT, any contracts consuming too much energy will consume extra penalty energy.

USDT is 90% of the energy consumed on TRON (link), this TIP will just help turning TRON into Ethereum, where USDT payments for small value items (< 10 USDT) are unbearable pay via the TRON network, which will just push people to stop using TRON.

So, basically, this TIP is kneecapping one of the biggest uses of the TRON network without providing any benefits whatsoever to anyone else, nor any meaningful spam prevention system.

How was this TIP approved?

Their aim is to make USDT transactions more expensive, but they can't admit that. So, they offer some bs explanation about other "mythical" contracts consuming a lot of energy. They know that we know they know they are wrong. They just don't care. This is very common among all centralized cryptos.

@ethan1844
Copy link
Contributor

Close this issue as it is implemented by GreatVoyage-v4.7.0.1.
Check TIP detail at TIP-491
Check implementation PR at tronprotocol/java-tron#4873

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants
@dwjorgeb @otakuinny @daniel-cao-byte @ethan1844 and others