资源模型

tron有两种资源:

  • 带宽 Bandwidth
    • 固定免费额度
    • 质押获得
  • 能量 Energy
    • 固定免费额度
    • 质押获得

资源模型指什么,指tron链当中,每个账户account都拥有的除了代币之外的资源。
这个资源是在执行交易或智能合约时,进行消耗的资源,大白话就是交易都有手续费,这个资源就相当于免费给你的资源,可以用来抵销转账的手续费,每个账户免费额度有限。

tron链中主要有两种资源模型:带宽能量
这个模型对标以太坊的 gas 费。

什么意思呢?
举个例子,比如玩游戏,游戏人物都有蓝条,这个蓝条使有技术就会耗蓝,这个蓝量会在一定时间内自动恢复。
这个能量和带完是一样的道理。

能量消耗

会在24小时恢复
tron链当中,每个账户都会拥有一定的免费资源额度,如果用完免费额度,就用账户中的代币抵扣,这个免费额度会在24小时内逐步恢复到一个初始值。

恢复过程

免费的能量有多少?

免费带宽是个固定值:1500。

质押能量有多少?

这个值是个动态值,跟参与这个游戏的人数有关。
这里有一个概念,就是全网总能上限,就是全网最大的一个带宽上限。
每个人都能从里面分到一点资源,当作一种福利看。

能分到多少,就要看有多少人来分了。
当前全网上限43_200_000_000。怎么分呢,就要看能与的人有多少。

如果当前只有一个人参与质押,那这个人能获得全部:

一个用户质押时

两个用户质押时,按照质押比例,分平

两个用户质押时

以此类推

三个用户质押时

全网有很多用户时,每个用户只能分到一点:

全网

资源处理器 ResourceProcessor

资源模型:ResourceProcessor 是资源处理的抽象类,有两个子类:

  • BandwidthProcessor: 带宽处理器
  • EnergyProcessor: 能量处理器

看下继承关系:

资源处理器ResourceProcessor.jpg


带宽 Bandwidth

普通交易仅消耗Bandwidth points。一般的TRX转账,消耗的是带宽。
这个带宽是字节来算的,就是一个一个字节,是不是有点意思,还有这样算钱的。

交易以字节数组的形式在网络中传输及存储,一条交易消耗的 Bandwidth Points = 交易字节数 * Bandwidth Points费率。当前 Bandwidth Points费率 = 1.

带宽来源

Bandwidth Points的获取分两种,也是由这两部分组成:

  • 质押所得:通过质押TRX获取的Bandwidth Points: 动态计算得到
  • 固定免费额度:每个账号每天有固定免费额度的带宽: 1500

单个账户质押能获得多少?

质押TRX能获得多少Bandwidth Points

额度 = 质押的TRX / 整个网络质押的TRX总额 * 43_200_000_000
也就是所有用户按质押TRX平分固定额度的Bandwidth Points。

举个例子,就是举例:

有一个池子,里面的每天会产生100块钱的奖金,这个奖金是按在场人数来分钱,当:

  1. 只有一个人的时候,这个人能得到100块
  2. 当有两个人的时候,每个人能得到50块
  3. 当有三个人的时候,每个人能得到33.333块
  4. 当有四个人的时候,每个人能得到25块

依次类推。

所以全网带宽的上限为:43_200_000_000
个人账户每日拥有免费带宽上限为:1500
这里强调时间是因为,这些值是个变量,如果社区有人发起提案要求改变这个值,如果投票通过,值就会发生变化。

全网带宽上限.jpg

也可以通过钱包客户端来印证一下,打开钱包看一下:

钱包.jpg

这个测试账户的钱包里,带宽是1500,但是我没有质押TRX,但是能量是0,这个后面说。

带宽消耗

除了查询操作,任何交易都需要消耗 bandwidth points。

  1. TRX 转账
  2. TRC10 转账
  3. 创建账户

有一种特殊情况需要注意,如果是转账,包括普通转账或 TRC10 Token 转账,如果目标账户不存在,转账操作则会创建账户并转账,只会扣除创建账户消耗的Bandwidth Points,转账不会再消耗额外的Bandwidth Points.

还有个问题,如果1500用完了怎么办?
那就消耗账户中的TRX,换算成带宽。

带宽消耗计算规则

逻辑自上而下,资源哪个够就扣哪个,如果不足就扣费:

  1. 消耗交易发起者质押获取的Bandwidth Points。
  2. 尝试消耗交易发起者的免费Bandwidth Points。
  3. 尝试消耗交易发起者的TRX,交易的字节数 * 1000 sun,这里就是说要收钱了。

1000 sun又是个什么单位?
简单理解,就是对应以太坊的 gas 最小单位wei
Tron网络中,1 TRX = 1_000_000 Sun

带宽恢复

这个说比较容易理解,公式看起来费解。
在网络总锁定资金以及账户锁定资金不变的情况向,账户的带宽的已使用量随着时间增加而按比例衰减,24h 衰减到 0 。

如时间 T1 时刻,账户带宽已使用量为 U , 到 T1 + 12 h ,账户再次使用带宽 u , 此时账户已使用带宽为 U / 2 + u
具体公式如下:

$$
U^\prime = ( 1 - \frac{T_2 - T_1}{24h} ) * U + u
$$

公式是对应代码的,后面通过代码来验证这个公式。

注意,这里提到的是:再次使用,这个再次使用的时刻,就是计算恢复多少的时间起点,后面代码解释。
就是说使用了带宽之后会在24小时后已使用量恢复到了0。

业务处理调用栈:

1
2
3
4
5
带宽处理入口
Manager.processTransaction(...)
\---consumeBandwidth(trxCap, trace);
\---BandwidthProcessor processor = new BandwidthProcessor(chainBaseManager);
\---processor.consume(trx, trace);

主要处理方法:BandwidthProcessor.consume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Override
public void consume(TransactionCapsule trx, TransactionTrace trace)
throws ContractValidateException, AccountResourceInsufficientException,
TooBigTransactionResultException {
List<Contract> contracts = trx.getInstance().getRawData().getContractList();
// 获取交易字节长度
if (trx.getResultSerializedSize() > Constant.MAX_RESULT_SIZE_IN_TX * contracts.size()) {
throw new TooBigTransactionResultException();
}

long bytesSize;

if (chainBaseManager.getDynamicPropertiesStore().supportVM()) {
bytesSize = trx.getInstance().toBuilder().clearRet().build().getSerializedSize();
} else {
bytesSize = trx.getSerializedSize();
}

for (Contract contract : contracts) {
if (contract.getType() == ShieldedTransferContract) {
continue;
}
if (chainBaseManager.getDynamicPropertiesStore().supportVM()) {
bytesSize += Constant.MAX_RESULT_SIZE_IN_TX;
}

logger.debug("trxId {}, bandwidth cost: {}", trx.getTransactionId(), bytesSize);
trace.setNetBill(bytesSize, 0);
byte[] address = TransactionCapsule.getOwner(contract);
AccountCapsule accountCapsule = chainBaseManager.getAccountStore().get(address);
if (accountCapsule == null) {
throw new ContractValidateException("account does not exist");
}
long now = chainBaseManager.getHeadSlot();
if (contractCreateNewAccount(contract)) {
consumeForCreateNewAccount(accountCapsule, bytesSize, now, trace);
continue;
}

if (contract.getType() == TransferAssetContract && useAssetAccountNet(contract,
accountCapsule, now, bytesSize)) {
continue;
}

// 扣质押获得的带宽
if (useAccountNet(accountCapsule, bytesSize, now)) {
continue;
}
// 扣免费代宽
if (useFreeNet(accountCapsule, bytesSize, now)) {
continue;
}

// 扣TRX
if (useTransactionFee(accountCapsule, bytesSize, trace)) {
continue;
}

// 都不足,就抛异常
long fee = chainBaseManager.getDynamicPropertiesStore().getTransactionFee() * bytesSize;
throw new AccountResourceInsufficientException(
"Account has insufficient bandwidth[" + bytesSize + "] and balance["
+ fee + "] to create new account");
}
}

能量 Energy

智能合约运行时执行每一条指令都需要消耗一定的系统资源,资源的多少用Energy的值来衡量。
作用:用户处理交易所需要消耗的资源。

Bandwidth Point表示带宽资源,Energy表示CPU和存储资源。

普通交易仅消耗:Bandwidth points
智能合约的操作不仅要消耗Bandwidth points,还会消耗Energy

合约消耗资源

能量的处理也很复杂,可以单独再提练一下,写太多不利于别人理解。

资源恢复

主要方法:ResourceProcessor.increase。
这个方法的实际就是上面提到的恢复模型,是一个滑动窗口算法。

在网络总锁定资金以及账户锁定资金不变的情况向,账户的带宽的已使用量随着时间增加而按比例衰减,24h 衰减到 0。
如时间 T1 时刻,账户带宽已使用量为 U, 到T1 + 12h,账户再次使用带宽u, 此时账户已使用带宽为 U/2 + u

$$
U^\prime = ( 1 - \frac{T_2 - T_1}{24h} ) * U + u
$$

U': 恢复的量
T1: 上次操作区块时间
U: 上一次使用量,
T2: 当前区块时间
u: 本次使用量

理解为每24h,用户已使用的带宽值重置为0。
大白话就是,你花了多少质押的带宽,24小时后给你恢复。

验证公式

参考链接

官方文档:https://tronprotocol.github.io/documentation-zh/mechanism-algorithm/resource/
资源模型源码: https://github.com/tronprotocol/java-tron/blob/develop/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java
transcan交易查询: https://tronscan.org/#/transaction/0c5e9247f8515f2b5e9d9e50930b3958917e059a13a29ff282829ac4027c0311