帖子
分享您的知识。
+15
Sui 事务失败:为另一笔交易保留的对象
我在JsonRpcError
尝试在 Sui 上执行交易时遇到了持久问题. 该错误表明对象是为另一个事务保留的,尽管我已经实现了延迟的顺序事务处理.
JsonRpcError: Failed to sign transaction by a quorum of validators because one or more of its objects is reserved for another transaction. Other transactions locking these objects:
- AV7coSQHWg5vN3S47xada6UiZGW54xxUNhRv1QUPqWK (stake 33.83)
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
- 0x1c20f15cbe780ee7586a2df90c1ab70861ca77a15970bea8702a8cf97bd3eed9
我试过了: -顺序事务执行(等待前一笔交易完成) -增加了交易之间的 3 秒延迟
而且仍然会持续出现同样的错误.
使用 Sui RPC 提交交易. 同一个对象 ID 在锁定列表中多次出现. 即使仔细安排交易顺序,也会出现错误.
- 是什么导致对象被 “保留” 用于其他交易?
- 在交易中使用对象之前,如何正确检查对象是否可用? 3.是否有在 Sui 中处理对象锁的最佳实践?
- 这可能与交易终结时间有关吗?
以前有人遇到过这个问题吗?如果您对Sui交易中的适当对象管理有任何见解,将不胜感激!
- Sui
- Transaction Processing
- Move
答案
8Sui 使用乐观并发控制,这意味着在事务中使用对象时会被锁定,直到该事务完成或到期.
即使你在两次事务之间等待 3 秒钟,如果前一个事务尚未完成,对象仍处于锁定状态. 这意味着交易仍处于待处理状态,并且持有对该对象的独占访问权限.
如何检查对象是否可用
使用 Sui RPC 方法:
sui_getObject
查看"status": "Locked"
或的回复"owner": "locked"
.
请求示例:
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getObject",
"params": ["0x...object_id..."]
}
如果已锁定,请稍等片刻,稍后重试.
嘿,你尝试的交易速度太快了,对象被锁定了.
尝试一次发送一个包含相同对象的交易,如果你发送两个交易,一些验证者可能会接受第一笔交易,有些验证者可能会接受第二笔交易,而你的对象将被锁定,因为每笔交易需要66.7%的验证器,而你可能只得到50%.
=> 等着纪元重置,很快就到了
查看更多:https://forums.sui.io/t/beginner-tutorial-error-when-deploying-simple-sui-package/44842
该错误表示您的事务使用的对象仍被先前尚未完成的事务锁定. 即使有延迟,在这些交易在链上完成之前,对象仍会被保留.
要修复:
- 在再次使用对象之前,请务必确认先前涉及对象的交易已完全完成.
- 通过 Sui RPC 检查物体状态,确保它们已解锁. 3.避免在相同的对象上发送并行或快速交易.
- 使用退避和最终性检查来实现重试,而不是固定延迟.
这种锁定可以防止更新冲突,这在 Sui 的对象模型中是正常的. JsonRpcError
正确的排序和最终性确认是避免的关键.
如果您尝试同时运行两个事务(例如,在前一个事务完成之前启动一个),就会发生此错误. 如果您重试运行发布事务,但之前或同时未运行其他事务,则该事务应该会成功. 你可能还需要从水龙头中获取更多的气体(或者等一天——等待时代结束——物体才能解锁)
当您运行涉及您的地址拥有的对象(例如气体对象)的交易时,验证器会保留该对象的最新版本供其签署的交易使用. 如果你尝试同时运行两个交易,并且它们引用同一个对象,那么它们将相互竞争以获取验证者的签名. 幸运的是,其中一笔交易获胜并运行,而另一笔交易未能获得足够的签名. 在不愉快的情况下,两笔交易都可能无法获得足够的签名(如果两笔交易都获得了验证者三分之一以上的签名,则任何一笔交易都无法超过三分之二,这是阈值),这被称为模棱两可,从那时起,作为两笔交易输入的对象不能用于任何其他交易.
在时代结束时(它们大约持续一天——你可以在 https://suiexplorer.com 上查看下一个纪元变更的进度),所有锁都被释放,所以你可以再次使用这些物体,但是如果你自上次尝试以来没有发生过纪元变化,你将需要获得更多的汽油.
在 Sui 网络上,当交易尝试访问已参与另一项正在进行的交易的对象时,该网络会返回预留错误. 这是 Sui 以对象为中心的执行模型的直接结果. 任何改变或消耗对象的交易都必须获得对该对象的独占锁. 提交此类交易后,该对象将保持保留状态,直到交易最终完成. 这种保留可以防止其他交易过早地与同一个对象进行交互,从而确保原子性并避免竞争条件. 但是,如果由于网络延迟、验证器延迟或意外执行行为而导致最终确定延迟,则对象的锁定时间可能会超过预期.
要确定对象是否处于可用状态,您可以sui_getObject
通过 Sui 的 JSON-RPC 接口或 Sui CLI 使用该方法. 检查对象的所有者字段或事务锁定元数据可以清楚地了解其状态. 如果该对象似乎正在使用中或未将所有权返回到预期地址,则该对象可能仍处于保留状态. 一种更强大的方法包括以编程方式轮询网络,直到对象的锁定被释放,这在自动流程或协调多个连续事务时尤其有用.
有效处理这些锁需要一些关键练习. 在先前涉及可变对象的操作完全完成之前,避免在交易中重复使用可变对象,这一点至关重要. 你应该考虑尽可能使用单独或轮换的汽油币和物体实例,以减少争用. 此外,整合重试逻辑和智能轮询机制(例如指数退避)可以最大限度地降低交易失败率. 当事务并行编排时,隔离每个进程或线程的对象使用情况可大大提高可靠性.
值得注意的是,这种行为与交易的最终性密切相关. 即使交易被接受到内存池并进行了广播,在网络的验证者法定人数确认交易的最终状态之前,关联的对象锁定仍然有效. 任何事先重用该对象的尝试,无论使用基于时间的任意延迟,都可能导致预订错误.
为了简化对象锁定处理,这里有一个 Bash 实用程序,你可以在开发工作流程中使用. 它轮询全节点并等待对象解锁后再继续交易. 这样可以确保对象可以安全地重复使用,并减少过早失效.
Bash 脚本:wait-for-unlock.sh
#!/bin/bash
# Sui Fullnode RPC endpoint (adjust as needed)
RPC_ENDPOINT="https://fullnode.testnet.sui.io:443"
# Checks if object is locked
is_locked() {
obj_id=$1
lock_info=$(curl -s -X POST $RPC_ENDPOINT \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "sui_getObject",
"params": ["'"$obj_id"'"],
"id": 1
}')
echo "$lock_info" | grep -q '"owner":{"AddressOwner"' && return 1 || return 0
}
# Waits until each object is no longer locked
wait_for_unlock() {
for obj_id in "$@"; do
echo "Checking object $obj_id..."
while is_locked $obj_id; do
echo "Object $obj_id is still locked. Retrying in 2s..."
sleep 2
done
echo "Object $obj_id is now unlocked."
done
}
# Main handler
main() {
if [ $# -eq 0 ]; then
echo "Usage: $0 <object_id_1> <object_id_2> ..."
exit 1
fi
wait_for_unlock "$@"
echo "All objects unlocked. Safe to proceed with transaction."
}
main "$@"
要使用该脚本,只需使其可执行并提供要跟踪的对象 ID 即可. 它将轮询直到待处理的交易不再保留这些对象,从而确保您的下一笔交易顺利进行.
chmod +x wait-for-unlock.sh
./wait-for-unlock.sh 0xYOUR_OBJECT_ID
这种类型的自动化在集成到部署脚本或 CI/CD 管道中时尤其有效,可为高吞吐量或多用户应用程序提供一层弹性.
你只是他的错误,因为你正在尝试同时运行多笔交易,可能是在尝试发送捆绑交易.
- 是什么导致对象被 “保留” 用于其他交易?
答案:该错误表示您的交易尝试使用某个对象(代币、NFT 等),在本例中为您的汽油币 (tx.gas
),该对象已被 Sui 网络上的另一项正在进行的交易 “锁定”.
Sui 对象每笔交易只能一次性使用,在锁定它们的交易得到确认或到期之前,你不能立即再次使用它们(例如,当你最近发送交易时,网络可能仍在处理它,因此在交易完全执行之前,代币会被临时保留).
- 在交易中使用某个对象之前,如何正确检查它是否可用?
答案:您等待第一笔交易完成或失败,这意味着当tx.gas
交易完成或失败时,网络预留的汽油币 () 已暂时释放. 您可以使用以下方法检查交易状态:
bash
sui client tx-status <digest>
3.是否有在 Sui 中处理对象锁的最佳实践?
答案:是的,在执行新交易之前,请等待较早的交易完成或失败(在测试网上最多可能需要20秒),并且在执行新交易之前,您可以随时确认交易已经完成. 你也可以尝试使用SPLIT YOUR SUI INTO MULTIPLE GAS COINS
这种方式,即使一枚硬币被锁定,也可以立即使用另一枚硬币
js
const tx = new Transaction();
const [newGas] = tx.splitCoins(tx.gas, [tx.pure.u64(1_000_000_000n)]); // 1 SUI
tx.transferObjects([newGas], tx.pure.address(ownAddress));
运行一次,你的账户中就会有多个小汽油币.
- 这可能与交易最终确定时间有关吗?
答案:是的,这与交易的最终性有关,我猜交易之间增加的 3 秒延迟不足以让交易完全完成,你可以随时使用以下命令查看交易状态 sui client tx-status <digest>
你遇到的错误是由于 Sui 的交易锁定机制造成的,该机制可以防止双重支出并确保原子操作. 让我解释一下到底发生了什么以及如何解决这个问题.
了解 Sui 中的交易锁定 当交易提交到 Sui 网络时,验证器会锁定引用的自有对象,以防止并发修改. 这种锁定机制对于防止双重支出和确保交易原子性至关重要.
为什么当前的解决方案不起作用 你目前在交易之间增加 3 秒延迟的方法无效,因为:
- 交易最终完成时间可能超过 3 秒.
- 多个交易可能会争夺同一个对象. 3.该网络不保证交易完成后对象的即时可用性.
分步解决方案
- 实施适当的交易状态检查;
async function waitForTransaction(txDigest) {
const MAX_RETRIES = 30;
const RETRY_DELAY_MS = 1000;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const result = await suiClient.transactionBlock({
digest: txDigest,
options: { showEffects: true }
});
if (result.status === 'SUCCESS') {
return result;
}
if (attempt === MAX_RETRIES - 1) {
throw new Error(`Transaction ${txDigest} failed`);
}
} catch (error) {
if (error.message.includes('not found')) {
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
continue;
}
throw error;
}
}
}
-
交易前检查目标可用性;
async function checkObjectAvailability(objectId) {
try {
const result = await suiClient.objects({
ids: [objectId],
options: { showPreviousVersion: true }
});
// Object is available if we can fetch it successfully
return result.details.length > 0;
} catch (error) {
return false;
}
}
3.使用 Backoff 实现交易重试;
async function executeTransactionWithRetry(txBuilder, signer) {
const MAX_RETRIES = 5;
const INITIAL_BACKOFF_MS = 500;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const tx = await txBuilder.sign();
const result = await suiClient.submitTransaction(tx);
// Wait for transaction completion
await waitForTransaction(result.digest);
return result;
} catch (error) {
if (attempt === MAX_RETRIES - 1) {
throw error;
}
const backoffMs = INITIAL_BACKOFF_MS * Math.pow(2, attempt);
console.log(`Attempt ${attempt + 1} failed, retrying in ${backoffMs}ms`);
await new Promise(resolve => setTimeout(resolve, backoffMs));
}
}
}
对象管理最佳实践
- 务必验证交易状态
-等待效果证书后再考虑交易完成
-在处理过程中定期检查交易状态
-对锁定对象实施正确的错误处理
- 对象可用性检查
-在创建交易之前验证对象的可用性
-处理准备过程中物体被锁定的情况
-考虑在智能合约中实现对象保留机制
3.交易排序
-使用效果证书作为完成的确认
-为重试实现指数回退
-通过适当的依赖关系管理维持交易顺序
- 错误处理
-区分临时锁定错误和永久故障
-使用退避实现正确的重试逻辑
-维护交易历史记录以进行调试
其他注意事项
- 时代变化
-交易在纪元转换期间可能会失败
-实现专门针对纪元相关故障的重试逻辑
-考虑交易时机与纪元边界的关系
- 网络拥塞
-在高峰时段监控网络负载
-根据网络状况调整退避策略
-考虑对高频交易实施速率限制
通过实施这些解决方案并遵循上述最佳实践,您应该能够有效地处理对象锁定场景并防止遇到JsonRpcError. 请记住,正确的错误处理和重试机制对于在Sui区块链上进行可靠的交易处理至关重要.
此错误表示您正在使用的对象仍被尚未完成的先前事务锁定. 即使有延迟,Sui仍会保留它们,直到连锁店确认完工.
要修复此问题,请确保先前使用这些对象的所有事务都已完全完成,然后再重复使用它们. 你可以通过 Sui RPC 查看他们的状态,看看他们是否已解锁. 避免发送涉及相同对象的多个或快速交易. 与其依赖固定延迟,不如使用带退避功能的重试,并在重试之前确认最终性.
这种锁定是 Sui 确保安全更新的方式的一部分,因此正确的排序和检查最终性是避免 jsonRpcError 的方法
你知道答案吗?
请登录并分享。
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
