Home指南API 参考手册
指南API 参考手册社区Discord博客FAQ漏洞赏金公告中心English(英文版)Log In
指南

FAQ

1.发送智能合约调用交易时,为什么要填写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错误?

答案 合约函数复杂度太高或者 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"410000000000000000000000000000000000000000T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb
"1"410000000000000000000000000000000000000001T9yD14Nj9j7xAB4dbGeiX9h8unkKLxmGkn
"2"410000000000000000000000000000000000000002T9yD14Nj9j7xAB4dbGeiX9h8unkKT76qbH
"dead"41000000000000000000000000000000000000deadT9yD14Nj9j7xAB4dbGeiX9h8upfCg3PBbY

4.调用合约,返回revert报错如何进行排查?

答案

  1. 首先根据txid,通过wallet/gettransactioninfobyid接口查询返回的结果中contractResult字段,如果字段不为空, 可以看到 message 的 abi 编码值,将编码值转成字符串,查找报错原因。 例如 txid:e5e013e81cb50a4c495a11c8130ad165a4e98d89b9e3fb5b79e6111bf23b31ed 返回contractResult数据:
"contractResult": [   "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001e536166654d6174683a207375627472616374696f6e206f766572666c6f770000"
]

1e536166654d6174683a207375627472616374696f6e206f766572666c6f77转成字符串是:SafeMath: subtraction overflow,这笔交易是因为转账时转出地址做减法操作时溢出,具体原因可能是余额不足,需要检查该地址的余额及转出金额。其他报错可根据具体报错原因进行排查。

  1. 如果contractResult为空,则可能是合约中不带message的require断言失败导致,需要查看合约源码进行分析。

5.如何计算调用合约时消耗的带宽及能量?

答案 交易消耗的带宽数量等于上链的交易所占用的字节数,其中包括三部分:交易的raw_data,交易签名以及交易结果。这三部分进行 protobuf 序列化编码后所占的字节数即为交易所消耗的带宽数量。下面分别以 Trident 和 TronWeb 为例说明如何进行带宽预估:

  1. 使用 Trident 预估带宽 Trident 提供带宽预估接口 estimateBandwidth,参数为已签名的交易。接口实现如下:
public long estimateBandwidth(Transaction txn)
{
    long byteSize = txn.toBuilder().clearRet().build().getSerializedSize() + 64;
    return byteSize;
}

其中,64为交易结果所占的字节数。

  1. 使用 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报错?

答案

  1. 如果发起调用的地址没有足够的 TRX 来支付所需的能量(Energy)或带宽(Bandwidth)费用,合约调用将会失败。为确保交易成功,请始终在调用地址中保持充足的 TRX 余额。关于资源消耗的估算,请参考计算带宽和能量使用
  2. 如果在 TRX 余额充足的情况下交易仍然失败,问题可能源于手续费上限(feeLimit)设置过低。feeLimit参数决定了可用于支付资源不足的 TRX 最大数量。要解决此问题,请提高您的feeLimit值,以确保交易成功处理。
  3. 获取更多能量(低能量解决方案) 如果您经常遇到能量不足的情况,可以通过以下可靠方法获取更多能量:
  • 质押 TRX 以获取能量和投票权:通过 Tronscan 浏览器或 TronLink 钱包直接进行质押。此方法简单、安全,并允许您通过投票权参与TRON网络治理。详细指南请参考在TRON网络上进行质押
  • 通过 JustLend DAO 能量租赁市场租赁能量:无需冻结资产即可即时获取能量。此方法提供快速、灵活的能量来源,价格透明且租赁条款可调。更多信息请参考能量租赁文档

8.如何解决节点区块同步慢或者停止同步问题?

答案

  1. 提高机器配置,推荐 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
  1. 提高验证交易的时间容忍度 节点配置文件中maxTimeRatio配置项的值提高到 20.0 或以上。
vm = {
  supportConstant = false
  minTimeRatio = 0.0
  maxTimeRatio = 20.0
  saveInternalTx = false
}
  1. 修改java-tron的启动参数,增加并行垃圾回收参数:XX:+UseConcMarkSweepGC 及-Xmx参数 类似:
java -Xmx24g -XX:+UseConcMarkSweepGC -jar FullNode.jar -c .....

其中:

  • -XX:+UseConcMarkSweepGC 要放在 -jar 参数前面,不能放在最后面;
  • -xmx 可以设置成物理内存的 80%。

9.如何解决SERVER_BUSY报错?

答案 如果节点未处理的交易数量超过 2000 会返回 server busy 错误, 根据机器情况,可以通过修改节点配置文件中的node.maxTransactionPendingSize来增加 PendingSize。 比如设置 node.maxTransactionPendingSize = 5000


10.如何解决 TronGrid 503 Service Temporarily Unavailable报错?

答案 为了保证请求资源的合理分配,目前 TronGrid 对所有请求都有IP频率限制。超出访问频率限制后,TronGrid 将返回 4xx 或 5xx 错误码。解决方法为:

  • 首先,请确保您在 URL 中使用了 API Key。没有 API Key 的请求将受到严格的速率限制,甚至被完全拒绝。
  • 其次,可以适当降低请 TronGrid 请求频率,比如,在 Dapp 启动时限制请求次数;不要时时轮训 TronGrid,因为 TRON 网络 3s 左右才生产一个区块,因此,以更快的速度请求新数据通常是没有意义的。

11.如何解决调用合约pureView方法时返回的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. 常见广播错误码说明

错误码 原因 解决方案

SIGERROR

签名错误

  1. 检查用以签名的私钥是否和发起交易的地址一致
  2. 检查私钥格式是否错误

BANDWITH_ERROR

账户中的资源不足,并且没有足够的 TRX 燃烧

向账户中充值 TRX,或者质押以获取资源,或者用其他账号代理资源给此账号

DUP_TRANSACTION_ERROR

广播了相同哈希的交易

  1. 重新创建交易
  2. 换一个节点重新广播

TAPOS_ERROR

reference block 与交易不在同一条链上

  1. 如果是通过自建节点API创建交易,可以修改节点配置 trx.reference.block = "solid"
  2. 如果是本地创建的交易,建议引用最新已固化的区块

TOO_BIG_TRANSACTION_ERROR

交易过大,可能是交易的 memo 过长

减少交易的大小

TRANSACTION_EXPIRATION_ERROR

交易过期。节点配置中,默认的交易过期时间是 60 秒,如果交易生成与广播的间隔超过此时间即为超时

在交易过期前广播交易。

  1. 如果是通过自建节点API创建交易,可以更改节点配置trx.expiration.timeInMilliseconds=60000,默认为 60 秒
  2. 如果是通过公共节点API创建交易,则无法更改节点配置,即无法更改交易的过期时间
  3. 如果本地构建的交易,可以将交易过期时间调大

SERVER_BUSY

网络繁忙

等待,错开网络繁忙的时间再重新发送交易

NOT_ENOUGH_EFFECTIVE_CONNECTION

节点未同步到最新区块

通过 Tronscan 查询当前的区块高度,并与本地节点 /wallet/getnowblock 的结果进行比较

OTHER_ERROR

未知错误

只能通过节点日志来获取到详细的错误信息

NO_CONNECTION

节点无可用的连接

配置 seed,重启节点

CONTRACT_EXE_ERROR

一般由于运行时异常、或者非法 protobuf 异常导致的合约执行失败

可以通过节点日志来获取到详细的错误信息

BLOCK_UNSOLIDIFIED

节点未固化的区块数量过多且超过了node.maxUnsolidifiedBlocks所设置的阈值(默认值为 54)

等待,等节点状态恢复后再广播交易。如果是自建节点,也可以通过配置node.unsolidifiedBlockCheck关闭未固化区块数量检查功能,或者通过node.maxUnsolidifiedBlocks设置一个更小的阈值

CONTRACT_VALIDATE_ERROR

交易(系统合约)验证失败

任何的参数错误都可能导致这个错误,需要解析错误消息查看详情。常见错误消息见下表

CONTRACT_VALIDATE_ERROR常见错误说明

错误信息说明
account does not exist发起交易的账号未激活
Validate *** error, no OwnerAccountOwner 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 代币的两笔转账交易的能量消耗差异,主要由两个因素导致:接收账户的存储状态 和 动态能量模型。

  1. 存储状态差异(SSTORE 指令能量消耗)

在 TVM 中,SSTORE 指令用于设置合约存储中某个 slot 的值,其能量消耗取决于该 slot 的状态:

  • 原值为 0 且 新值大于 0:执行 SSTORE 消耗 20,000 能量
  • 原值不为 0:执行 SSTORE 消耗 5,000 能量

TRC20 代币转账一般会涉及 SSTORE 指令的执行(账户余额的变更)。因此,根据实际存储状态的不同,转账交易的能量消耗会有差异。

  1. 动态能量模型的影响

对于热门 TRC20 代币合约,由于受 动态能量模型 的影响,即使是相同的操作,在不同时间执行也可能产生不同的能量消耗。

  1. 示例说明

以热门的 USDT 合约为例,其受动态能量模型的影响,目前一笔 USDT 转账交易的能量消耗值通常处于以下数量级:

  • 向 USDT 余额 > 0 的地址转账: 约 64,000 Energy 左右,示例交易
  • 向 USDT 余额 = 0 的地址转账: 约 130,000 Energy 左右,示例交易

注意: 实际能量消耗是变动的,这些数值仅供参考,代表消耗的数量级。消耗值会随着 USDT 合约的 energy_factor变化而波动,靠近此数量级范围的消耗均属合理,具体取决于网络当时的实时状态。

相比之下,对于未触发动态能量机制的 TRC20 代币合约,其能量消耗相对固定,不会受到动态能量模型的影响。以 BTT 为例,其转账能量预估值为:

  • 向 BTT 余额 > 0 的地址转账:约 13,253 Energy,示例交易
  • 向 BTT 余额 = 0 的地址转账:约 28,253 Energy,示例交易

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 结构如下,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. 如何提升节点的网络稳定性与资源使用的可控性?

答案

当节点流量异常或者堆外内存不足时,开发者可以从以下两个方面进行配置,以提升节点在实际运行中的稳定性和对资源使用的可控性。

  1. 限制 JVM 可分配的堆外内存上限 通过设置 JVM 参数 MaxDirectMemorySize,可有效控制节点的堆外内存使用,避免资源占xx用异常。通常将该参数设置为物理内存的 1/4。例如,若节点配备 16GB 物理内存,可将该参数设置为 4GB,节点启动命令如下:
java -XX:MaxDirectMemorySize=4g -jar FullNode.jar -c config.conf
  1. 限制出入口 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:限速维度,如 srcipdstipsrcipdstport
  • --hashlimit-above:速率限制
  • --hashlimit-burst:允许的突发上限,即瞬时峰值
  • -j:超出--hashlimit-above设定的速率后的处理动作,常用为DROP,表示直接丢弃数据包

23. 当节点停止同步并在日志中出现 different resultCode 字样时,该如何处理?

答案

在节点同步区块的过程中,如果日志中出现 different resultCode字样,说明本地节点在执行某笔交易时,其结果与区块中记录的预期结果不一致,从而导致节点无法继续同步区块。常见原因及对应解决方案如下:

  1. 预期结果为 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)。修改配置后,请重启节点以使配置生效。

  1. 预期结果为 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

原因:本地节点的各数据库之间存在数据不一致,导致本应执行失败的交易被错误地判定为执行成功。数据库损坏的原因可能包括异常关闭节点、硬件故障等多种情况。

解决方法:建议重新下载最新的数据库快照后重启节点,以确保各数据库状态的一致性。

  1. 预期结果为 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 参数后重启节点,即可恢复正常同步。