FAQ
1.发送智能合约调用交易时,为什么要填写fee_limit字段?
fee_limit字段?答案 防止合约调用交易消耗过多的 Energy。
解释
fee_limit表示的是合约调用者愿意承担这笔交易消耗的最大 Energy 的值。
fee_limit单位是 sun,最大可以设为 1.5e10,如果用户不设置fee_limit,默认值是 0。比如在交易中fee_limit设置为 1000 sun,那么表示合约调用者在这笔交易最多只會消耗 1000/100 Energy(目前 Energy 的单价是 100 sun)。
如果交易执行消耗的 Energy 数值超过了fee_limit*Energy单价,就会停止合约执行,并触发OUT_OF_ENERGY错误。
有些合约函数中会存在复杂的循环,用户在不知道的情况下误调用,可能会导致用户消耗过多的 Energy,于是用户可以设定这个fee_limit字段来设置一个上限。
有一些情况会导致所有的fee_limit被扣除:
- 合约执行过程中遇到非法指令
- 合约调用超时,触发
OUT_OF_TIME错误 - 如果你访问数组的索引太大或为负数(例如
x[i]其中i >= x.length或i < 0) - 如果你访问固定长度
bytesN的索引太大或为负数 - 如果你用零当除数做除法或模运算(例如 5 / 0 或 23 % 0 )
- 如果你移位负数位
- 如果你将一个太大或负数值转换为一个枚举类型
- 如果你调用未被初始化的内部函数类型变量
- 合约中调用
assert的参数(表达式)最终结果是false - 发生
JVMStackOverFlowException - 发生
OutOfMem异常,即内存超过了 3M - 合约运行过程中,发生了加法等溢出
- 在动态能量模型启用后,不同维护周期同一合约调用的能量消耗也是动态的,
fee_limit需要做相应的调整
2.调用合约时,为什么会触发OUT_OF_TIME错误?
OUT_OF_TIME错误?答案 合约函数复杂度太高或者 SR 节点的性能波动。
解释
目前 TRON 有一个全局设定, 智能合约调用的交易,执行时间不能超过 80ms, 这个 80ms 参数可以通过 SR 投票修改。如果合约代码复杂很高,导致执行时间超过 80ms,会触发OUT_OF_TIME错误,同时扣除全部fee_limit费用。
如果同一个合约函数,有时会触发OUT_OF_TIME错误,有时不会触发OUT_OF_TIME错误,说明合约代码的复杂度在一个临界值,由于不同的 SR 的机器性能不一样, 所以会导致间歇性触发。
另外需要注意的是由于 SR 机器性能的波动,会存在很小的概率导致复杂度非常低的合约函数调用也会触发OUT_OF_TIME错误。建议用户根据合约复杂度设置适当的fee_limit,防止fee_limit设置的过大引起过大的损失。
3. 常见的用于代币销毁的地址
遵循行业惯例,诸如 “0”、“1”、“2” 以及 “dead” 等地址被视为通用空地址或代币销毁地址。它们常与代币销毁(用于减少总供应量)、代币铸造或创世事件等关键操作相关联。
这些地址不归属于任何用户,任何转入其中的资产将被永久锁定,从而无法访问和恢复。
| Hex格式 | Base58Check格式 | |
|---|---|---|
| "0" | 410000000000000000000000000000000000000000 | T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb |
| "1" | 410000000000000000000000000000000000000001 | T9yD14Nj9j7xAB4dbGeiX9h8unkKLxmGkn |
| "2" | 410000000000000000000000000000000000000002 | T9yD14Nj9j7xAB4dbGeiX9h8unkKT76qbH |
| "dead" | 41000000000000000000000000000000000000dead | T9yD14Nj9j7xAB4dbGeiX9h8upfCg3PBbY |
4.调用合约,返回revert报错如何进行排查?
revert报错如何进行排查?答案
- 首先根据txid,通过
wallet/gettransactioninfobyid接口查询返回的结果中contractResult字段,如果字段不为空, 可以看到 message 的 abi 编码值,将编码值转成字符串,查找报错原因。 例如txid:e5e013e81cb50a4c495a11c8130ad165a4e98d89b9e3fb5b79e6111bf23b31ed返回contractResult数据:
"contractResult": [ "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001e536166654d6174683a207375627472616374696f6e206f766572666c6f770000"
]
将1e536166654d6174683a207375627472616374696f6e206f766572666c6f77转成字符串是:SafeMath: subtraction overflow,这笔交易是因为转账时转出地址做减法操作时溢出,具体原因可能是余额不足,需要检查该地址的余额及转出金额。其他报错可根据具体报错原因进行排查。
- 如果
contractResult为空,则可能是合约中不带message的require断言失败导致,需要查看合约源码进行分析。
5.如何计算调用合约时消耗的带宽及能量?
答案
交易消耗的带宽数量等于上链的交易所占用的字节数,其中包括三部分:交易的raw_data,交易签名以及交易结果。这三部分进行 protobuf 序列化编码后所占的字节数即为交易所消耗的带宽数量。下面分别以 Trident 和 TronWeb 为例说明如何进行带宽预估:
- 使用 Trident 预估带宽 Trident 提供带宽预估接口 estimateBandwidth,参数为已签名的交易。接口实现如下:
public long estimateBandwidth(Transaction txn)
{
long byteSize = txn.toBuilder().clearRet().build().getSerializedSize() + 64;
return byteSize;
}
其中,64为交易结果所占的字节数。
- 使用 TronWeb 预估带宽
function estimateBandwidth(signedTxn)
{
var DATA_HEX_PROTOBUF_EXTRA = 3;
var MAX_RESULT_SIZE_IN_TX = 64;
var A_SIGNATURE = 67;
var len = signedTxn.raw_data_hex.length /2 + DATA_HEX_PROTOBUF_EXTRA + MAX_RESULT_SIZE_IN_TX ;
var signatureListSize = signedTxn.signature.length
console.log(signatureListSize)
for(let i=0;i<signatureListSize;i++)
{
len += A_SIGNATURE;
}
return len;
}
合约交易消耗的能量根据合约执行的指令来计算,不同的指令消耗的能量不一样 ,越复杂的合约消耗的能量越多。合约消耗的能量可以通过在测试网测试或者通过 Tronscan 查看该合约的历史交易来预估,或者通过调用 API 来预估,关于如何使用 API 预估交易的能量消耗量请参考 这里。
6.交易广播成功后,为什么链上查询不到?
答案 交易广播成功但是没有上链,是因为节点所在网络或其他未知原因导致该交易没有广播给 SR 节点,这种情况下,因为交易都有有效期,最好加上延迟判断,超过交易的有效期再加一点额外的时间还未上链,可以判断这个交易已经超过有效期,可以重新发起交易,或者在有效期内重新广播该交易。
7.如何解决OUT_OF_ENERGY报错?
OUT_OF_ENERGY报错?答案
- 如果发起调用的地址没有足够的 TRX 来支付所需的能量(Energy)或带宽(Bandwidth)费用,合约调用将会失败。为确保交易成功,请始终在调用地址中保持充足的 TRX 余额。关于资源消耗的估算,请参考计算带宽和能量使用。
- 如果在 TRX 余额充足的情况下交易仍然失败,问题可能源于手续费上限(
feeLimit)设置过低。feeLimit参数决定了可用于支付资源不足的 TRX 最大数量。要解决此问题,请提高您的feeLimit值,以确保交易成功处理。 - 获取更多能量(低能量解决方案) 如果您经常遇到能量不足的情况,可以通过以下可靠方法获取更多能量:
- 质押 TRX 以获取能量和投票权:通过 Tronscan 浏览器或 TronLink 钱包直接进行质押。此方法简单、安全,并允许您通过投票权参与TRON网络治理。详细指南请参考在TRON网络上进行质押。
- 通过 JustLend DAO 能量租赁市场租赁能量:无需冻结资产即可即时获取能量。此方法提供快速、灵活的能量来源,价格透明且租赁条款可调。更多信息请参考能量租赁文档。
8.如何解决节点区块同步慢或者停止同步问题?
答案
- 提高机器配置,推荐 16core 32GRAM 1T 硬盘(ssd)。确保单个物理 cpu 的逻辑 core 的数量大于等于 16,下面的命令的结果中,cpu cores 和 siblings 两者的最大值需要大于等于 16。
$cat /proc/cpuinfo | grep -e "cpu cores" -e "siblings" | sort | uniq
cpu cores : 8
siblings : 16
- 提高验证交易的时间容忍度
节点配置文件中
maxTimeRatio配置项的值提高到 20.0 或以上。
vm = {
supportConstant = false
minTimeRatio = 0.0
maxTimeRatio = 20.0
saveInternalTx = false
}
- 修改
java-tron的启动参数,增加并行垃圾回收参数:XX:+UseConcMarkSweepGC 及-Xmx参数 类似:
java -Xmx24g -XX:+UseConcMarkSweepGC -jar FullNode.jar -c .....
其中:
-XX:+UseConcMarkSweepGC要放在-jar参数前面,不能放在最后面;-xmx可以设置成物理内存的 80%。
9.如何解决SERVER_BUSY报错?
SERVER_BUSY报错?答案
如果节点未处理的交易数量超过 2000 会返回 server busy 错误, 根据机器情况,可以通过修改节点配置文件中的node.maxTransactionPendingSize来增加 PendingSize。 比如设置 node.maxTransactionPendingSize = 5000。
10.如何解决 TronGrid 503 Service Temporarily Unavailable报错?
503 Service Temporarily Unavailable报错?答案 为了保证请求资源的合理分配,目前 TronGrid 对所有请求都有IP频率限制。超出访问频率限制后,TronGrid 将返回 4xx 或 5xx 错误码。解决方法为:
- 首先,请确保您在 URL 中使用了 API Key。没有 API Key 的请求将受到严格的速率限制,甚至被完全拒绝。
- 其次,可以适当降低请 TronGrid 请求频率,比如,在 Dapp 启动时限制请求次数;不要时时轮训 TronGrid,因为 TRON 网络 3s 左右才生产一个区块,因此,以更快的速度请求新数据通常是没有意义的。
11.如何解决调用合约pure 和View方法时返回的Transaction中缺少Constant_result字段?
pure 和View方法时返回的Transaction中缺少Constant_result字段?答案
此问题目前只见于用户将java-tron版本从 GreatVoyage-v4.2.2(Lucretius) 或其他更高版本降级到 GreatVoyage-v4.2.1(Origen)(或者 GreatVoyage-v4.2.0(Plato) 版本)后,如果用户遇到此问题,需要使用修复工具对数据库进行修复,修复完成之后使用 GreatVoyage-v4.2.2.1(Epictetus) 或者更高版本即可正常启动节点。 详细操作步骤参看DBReqair.jar使用指南
12. 常见广播错误码说明
| 错误码 | 原因 | 解决方案 |
|---|---|---|
|
签名错误 |
|
|
账户中的资源不足,并且没有足够的 TRX 燃烧 |
向账户中充值 TRX,或者质押以获取资源,或者用其他账号代理资源给此账号 |
|
广播了相同哈希的交易 |
|
|
reference block 与交易不在同一条链上 |
|
|
交易过大,可能是交易的 memo 过长 |
减少交易的大小 |
|
交易过期。节点配置中,默认的交易过期时间是 60 秒,如果交易生成与广播的间隔超过此时间即为超时 |
在交易过期前广播交易。
|
|
网络繁忙 |
等待,错开网络繁忙的时间再重新发送交易 |
|
节点未同步到最新区块 |
通过 Tronscan 查询当前的区块高度,并与本地节点 /wallet/getnowblock 的结果进行比较 |
|
未知错误 |
只能通过节点日志来获取到详细的错误信息 |
|
节点无可用的连接 |
配置 seed,重启节点 |
|
一般由于运行时异常、或者非法 protobuf 异常导致的合约执行失败 |
可以通过节点日志来获取到详细的错误信息 |
|
节点未固化的区块数量过多且超过了 |
等待,等节点状态恢复后再广播交易。如果是自建节点,也可以通过配置 |
|
交易(系统合约)验证失败 |
任何的参数错误都可能导致这个错误,需要解析错误消息查看详情。常见错误消息见下表 |
CONTRACT_VALIDATE_ERROR常见错误说明
| 错误信息 | 说明 |
|---|---|
| account does not exist | 发起交易的账号未激活 |
| Validate *** error, no OwnerAccount | Owner address 不正确 |
| No contract or not a valid smart contract | 合约地址错误 |
| this node does not support constant | 节点的vm.supportconstant没有设置为true,常见于自建节点 |
| Not support *** transaction, need to be opened by the committee | 节点暂不支持该种类型的交易,需要通过超级代表委员会投票以支持该类型 |
| delegateBalance must be more than 1TRX | 代理的资源份额不应小于 1 TRX |
13.如何加快节点启动速度?
答案
对于使用 LevelDB 的节点,可以使用 LevelDB 启动优化工具 加快节点启动速度。该工具通过优化 manifest 文件大小和 LevelDB 初始化过程,降低内存占用,提高启动效率。详细操作请参见用户指南。 。
14.如何使用tronWeb调用ABI不在链上的合约?
答案 请参考文档 如何使用 TronWeb 调用 ABI 不在链上的合约。
15.如何通过扫块判断地址上资金的转入和转出?
答案 对于交易所或者钱包,通常需要获取一个账户的历史交易信息,或者监控一个账户地址资金的实时转入和转出情况,交易所或者钱包可以搭建一个全节点,然后通过解析历史区块来获取一个账户地址的交易记录,具体参考这里。
16.为什么同一个 TRC20 代币的两笔转账交易消耗的能量会不一样?
答案:同一个TRC20 代币的两笔转账交易的能量消耗差异,主要由两个因素导致:接收账户的存储状态 和 动态能量模型。
- 存储状态差异(
SSTORE指令能量消耗)
在 TVM 中,SSTORE 指令用于设置合约存储中某个 slot 的值,其能量消耗取决于该 slot 的状态:
- 原值为 0 且 新值大于 0:执行
SSTORE消耗 20,000 能量 - 原值不为 0:执行
SSTORE消耗 5,000 能量
TRC20 代币转账一般会涉及 SSTORE 指令的执行(账户余额的变更)。因此,根据实际存储状态的不同,转账交易的能量消耗会有差异。
- 动态能量模型的影响
对于热门 TRC20 代币合约,由于受 动态能量模型 的影响,即使是相同的操作,在不同时间执行也可能产生不同的能量消耗。
- 示例说明
以热门的 USDT 合约为例,其受动态能量模型的影响,目前一笔 USDT 转账交易的能量消耗值通常处于以下数量级:
注意: 实际能量消耗是变动的,这些数值仅供参考,代表消耗的数量级。消耗值会随着 USDT 合约的 energy_factor变化而波动,靠近此数量级范围的消耗均属合理,具体取决于网络当时的实时状态。
相比之下,对于未触发动态能量机制的 TRC20 代币合约,其能量消耗相对固定,不会受到动态能量模型的影响。以 BTT 为例,其转账能量预估值为:
17.为什么使用多重签名进行 Stake 2.0 质押会提示权限不足?
答案
主网 Stake 2.0 生效之前激活的账户,使用多重签名的方式发起 Stake 2.0 相关操作可能会提示权限不足。
原因是Stake 2.0 生效之前已激活账户的多重签名权限组不包含 Stake 2.0 相关操作的权限位,需要更新您帐户的多重签名设置,根据您的需求将 1 个或多个 Stake 2.0 相关权限位添加到您当前的多重签名权限组中,下面是 TronLink 钱包权限更新页面:
18. 如何解析交易中的 raw_data_hex 数据?
raw_data_hex 数据?答案
一个交易的 raw_data 结构如下,raw_data 序列化之后的结果是十六进制字符串 raw_data_hex。
{
"txID": "252c875d688c23789b531a89d7018b317764a753d611b0d7756f57f6d1b827e9",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"data": "a9059cbb00000000000000000000000074178942c355dc4b88f3a43499a18e4990ed6cf7000000000000000000000000000000000000000000000000000000012a05f200",
"owner_address": "41b3dcf27c251da9363f1a4888257c16676cf54edf",
"contract_address": "41eca9bc828a3005b9a3b909f2cc5c2a54794de05f"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}
],
"ref_block_bytes": "1b98",
"ref_block_hash": "4e1c1e7428d1d7a4",
"expiration": 1719223548000,
"fee_limit": 30000000,
"timestamp": 1719223489371
},
"raw_data_hex": "0a021b9822084e1c1e7428d1d7a440e0f0d5cd84325aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541b3dcf27c251da9363f1a4888257c16676cf54edf121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244a9059cbb00000000000000000000000074178942c355dc4b88f3a43499a18e4990ed6cf7000000000000000000000000000000000000000000000000000000012a05f20070dba6d2cd843290018087a70e"
}以下是使用 Trident SDK 解析交易中 raw_data_hex 的示例代码:
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import org.tron.trident.abi.TypeDecoder;
import org.tron.trident.abi.datatypes.Address;
import org.tron.trident.abi.datatypes.generated.Uint256;
import org.tron.trident.core.utils.ByteArray;
import org.tron.trident.crypto.Hash;
import org.tron.trident.proto.Chain.Transaction;
import org.tron.trident.proto.Contract;
import org.tron.trident.proto.Contract.TriggerSmartContract;
import java.math.BigInteger;
public class Demo {
public void parseRawDataHex() throws InvalidProtocolBufferException {
// 使用任意私钥初始化 ApiWrapper,此处私钥不影响解析过程
ApiWrapper client = ApiWrapper.ofNile("3333333333333333333333333333333333333333333333333333333333333333");
String rawDataHexString = "0a021b9822084e1c1e7428d1d7a440e0f0d5cd84325aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541b3dcf27c251da9363f1a4888257c16676cf54edf121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244a9059cbb00000000000000000000000074178942c355dc4b88f3a43499a18e4990ed6cf7000000000000000000000000000000000000000000000000000000012a05f20070dba6d2cd843290018087a70e";
Transaction.raw rawData = Transaction.raw.parseFrom(ByteArray.fromHexString(rawDataHexString));
System.out.println("引用区块字节:" + ApiWrapper.toHex(rawData.getRefBlockBytes()) +
"\n 引用区块哈希:" + ApiWrapper.toHex(rawData.getRefBlockHash()) +
"\n 过期时间:" + rawData.getExpiration() +
"\n 时间戳:" + rawData.getTimestamp() +
"\n 手续费上限:" + rawData.getFeeLimit() +
"\n 合约类型:" + rawData.getContract(0).getType() );
Transaction.Contract contract = rawData.getContract(0);
Any contractParameter = contract.getParameter();
switch (contract.getType()) {
case TriggerSmartContract:
TriggerSmartContract triggerSmartContract = contractParameter.unpack(TriggerSmartContract.class);
System.out.println("\n 合约地址:" + ApiWrapper.toHex(triggerSmartContract.getContractAddress()) +
"\n 所有者地址:" + ApiWrapper.toHex(triggerSmartContract.getOwnerAddress()) +
"\n 数据:" + ApiWrapper.toHex(triggerSmartContract.getData() ));
// 解码合约参数中的 data 字段
dataDecodingTutorial(ApiWrapper.toHex(triggerSmartContract.getData()));
break;
case TransferContract:
Contract.TransferContract transferContract = contractParameter.unpack(Contract.TransferContract.class);
break;
default:
break;
}
}
public void dataDecodingTutorial(String DATA) {
// DATA: a9059cbb00000000000000000000000074178942c355dc4b88f3a43499a18e4990ed6cf7000000000000000000000000000000000000000000000000000000012a05f200
String rawSignature = DATA.substring(0,8);
String functionSignatureExample = "transfer(address,uint256)"; //函数签名
String functionSelectorExample = Hash.sha3String(functionSignatureExample).substring(2,10); // 函数选择器
if(rawSignature.equals(functionSelectorExample))
{
Address rawRecipient = TypeDecoder.decodeAddress(DATA.substring(8,72)); //接收者地址
String recipient = rawRecipient.toString();
Uint256 rawAmount = TypeDecoder.decodeNumeric(DATA.substring(72,136), Uint256.class); //金额
BigInteger amount = rawAmount.getValue();
System.out.println("调用的函数: " + functionSignatureExample);
System.out.println("转账 " + amount + " 到 " + recipient);
}
}
}19. 如何为交易设置引用区块信息?
答案
请注意,交易的有效引用区块是最近的 65536 个区块,通常使用最新的固化块作为引用区块。以下是设置引用区块信息的示例代码。
public void setReference(long blockNum, byte[] blockHash) {
byte[] refBlockNum = ByteArray.fromLong(blockNum);
Transaction.raw rawData = this.transaction.getRawData().toBuilder()
.setRefBlockHash(ByteString.copyFrom(ByteArray.subArray(blockHash, 8, 16)))
.setRefBlockBytes(ByteString.copyFrom(ByteArray.subArray(refBlockNum, 6, 8)))
.build();
setRawData(rawData);
}20. 是否有办法在测试或调试时,临时绕过交易执行时间的限制?
答案
目前,智能合约交易的最大执行时间为 80 毫秒,如果超过此限制,交易将抛出 OUT_OF_TIME 异常。然而,在某些测试场景中(如涉及大量计算的查询操作)可能需要临时绕过执行时间限制。这可以通过使用 --debug 参数启动节点来实现。这样,节点在处理交易时将跳过 80 毫秒的超时检查,意味着将没有执行时间限制。然后,您可以使用 /wallet/triggerconstantcontract API 来查询合约数据以进行测试或调试。
请注意,当绕过交易执行时间限制时,如果收到的区块中包含一个预期结果为 OUT_OF_TIME 的交易,那么该交易的本地执行将产生不同的结果(因为 TVM 不再执行超时检查)。这会导致本地执行结果与区块中记录的结果之间存在差异,可能引发 different resultCode 错误,并导致节点停止同步。
因此,我们强烈建议您不要在主网节点上执行此类测试,因为这可能导致不可预测的行为。如果您需要测试复杂的合约或长时间运行的交易,我们建议您使用私有链环境。
21. 如何让节点停止在指定区块高度?
答案
为便于开发者进行数据备份或数据查询,节点支持在满足特定条件时自动停止运行。开发者可在节点配置文件中设置以下任一条件,控制节点的停止行为。一旦满足设定条件,节点将自动停止区块同步并退出运行。
node {
shutdown {
# 指定时间点停止(基于 Quartz 表达式)
BlockTime = "54 59 08 * * ?"
# 指定区块高度停止
BlockHeight = 33350800
# 启动后同步指定数量区块后停止
BlockCount = 12
}
}
注意:上述三个配置项中仅可选择其中一个进行设置。如同时配置多个条件,节点将启动失败并报错。
当节点依据设定条件成功停止并退出后,开发者可以执行数据备份,或重新启动节点以进行数据查询。若仅需进行查询操作,可在节点启动命令中添加参数 -p2p-disable true,以启用查询模式。在该模式下,节点不会启动网络模块,也不参与节点发现与区块同步,但仍保留接口服务,便于用户查询当前系统状态。示例启动命令如下:
java -jar FullNode.jar -c config.conf --p2p-disable true
22. 如何提升节点的网络稳定性与资源使用的可控性?
答案
当节点流量异常或者堆外内存不足时,开发者可以从以下两个方面进行配置,以提升节点在实际运行中的稳定性和对资源使用的可控性。
- 限制 JVM 可分配的堆外内存上限
通过设置 JVM 参数
MaxDirectMemorySize,可有效控制节点的堆外内存使用,避免资源占xx用异常。通常将该参数设置为物理内存的 1/4。例如,若节点配备 16GB 物理内存,可将该参数设置为 4GB,节点启动命令如下:
java -XX:MaxDirectMemorySize=4g -jar FullNode.jar -c config.conf
- 限制出入口 TCP 流量
为提升节点网络的可控性,避免资源异常占用,可通过系统的
iptables命令,结合hashlimit模块,对节点的出入口 TCP 流量进行限制。以下为出口与入口流量控制的示例配置,开发者可根据实际业务需求和带宽情况,灵活调整参数以实现最优效果。
示例一:限制出口 TCP 报文速率 将发送到每个目标 IP 的出口速率限制为每秒不超过 15MB,瞬时突发速率不超过 30MB。示例命令如下:
iptables -A OUTPUT -p tcp -m hashlimit \
--hashlimit-name out_limit \
--hashlimit-mode dstip \
--hashlimit-above 15mb/sec \
--hashlimit-burst 30mb \
-j DROP
示例二:限制入口 TCP 报文速率 将每个IP向本地 18888 端口发送的 TCP 报文速率限制为每秒不超过 300 个,瞬时峰值不超过 600 个。示例命令如下:
iptables -A INPUT -p tcp --dport 18888 -m hashlimit \
--hashlimit-name in_limit \
--hashlimit-mode srcip,dstport \
--hashlimit-above 300/sec \
--hashlimit-burst 600 \
-j DROP
hashlimit模块常用参数说明:
--hashlimit-name:规则名称,便于识别规则的用途--hashlimit-mode:限速维度,如srcip、dstip、srcip、dstport等--hashlimit-above:速率限制--hashlimit-burst:允许的突发上限,即瞬时峰值-j:超出--hashlimit-above设定的速率后的处理动作,常用为DROP,表示直接丢弃数据包
23. 当节点停止同步并在日志中出现 different resultCode 字样时,该如何处理?
different resultCode 字样时,该如何处理?答案
在节点同步区块的过程中,如果日志中出现 different resultCode字样,说明本地节点在执行某笔交易时,其结果与区块中记录的预期结果不一致,从而导致节点无法继续同步区块。常见原因及对应解决方案如下:
- 预期结果为
SUCCESS,但本地执行结果为OUT_OF_TIME
05:32:54.591 ERROR [sync-handle-block] [DB](Manager.java:1337) different resultCode txId: ae53f8a6394d7adcc2337e0e71724f520818161cbb1f6f5d556f873b08e17c99, expect: SUCCESS, actual: OUT_OF_TIME
原因:本地节点执行交易时超时,通常是由于机器性能不足所致。
解决方法:为确保主网节点的稳定运行,强烈建议使用至少 16 核 CPU 的机器,8 核 CPU 可视为最低配置要求。同时,也可以通过在配置文件中调整 maxTimeRatio 参数来提高交易验证的时间容忍度(例如,将 maxTimeRatio 的值设置为 50)。修改配置后,请重启节点以使配置生效。
- 预期结果为
OUT_OF_ENERGY,但本地执行结果为SUCCESS
11:29:18.294 ERROR [pool-48-thread-1] [DB](Manager.java:1306) different resultCode txId: fbe7109a993b52243dc4de4087967cebc74be739d725dc27e96eb757496bd359, expect: OUT_OF_ENERGY, actual: SUCCESS
原因:本地节点的各数据库之间存在数据不一致,导致本应执行失败的交易被错误地判定为执行成功。数据库损坏的原因可能包括异常关闭节点、硬件故障等多种情况。
解决方法:建议重新下载最新的数据库快照后重启节点,以确保各数据库状态的一致性。
- 预期结果为
OUT_OF_TIME,但本地执行结果为SUCCESS
06:22:13.575 ERROR [sync-handle-block] [DB](Manager.java:1323) different resultCode txId: 4ccf1feb7da348cef4190bc5d84d09d3c37160bfbdd21a0b5fd0b1d5005ead09, expect: OUT_OF_TIME, actual: SUCCESS
原因:检查是否使用 --debug 参数启动节点后出现同步中断。节点以 --debug 参数启动时,会跳过默认的 80ms 执行时间限制检查,这可能导致原本应超时而失败的交易在本地执行成功,从而导致结果与区块中记录的不一致,最终触发同步中断。
解决方法:在启动命令中移除 --debug 参数后重启节点,即可恢复正常同步。
Updated about 2 months ago