NEAR跨合约调用异常如何处理?
How to handle exceptions in NEAR cross contract calls?
如何在合约之间的异步调用链中捕获并处理异常?
假设我的事务发起了以下调用:
contractA.run()
-> do changes in contractA
-> calls contractB.run()
-> do changes in contractB
-> then calls another method on contractA: contractA.callback()
* callback() crashes
在 Promise 出现异常后,NEAR 不会回滚过去 Promise 中发生的更改。我也没有在 near-sdk.
中看到任何处理异常的方法
一个想法是 return 错误而不是抛出异常并创建一堆私有函数来更新错误值和添加/释放互斥体后的状态。然而,这并不能解决有时我们无法控制的问题,例如在外部智能合约中(例如,如果 contractB.do
会在上面的示例中出现恐慌)。
捕获异常的唯一方法是对生成异常的承诺进行回调。
在解释的场景中,contractA.callback()
应该不会崩溃。您需要足够仔细地构建合约,以避免回调失败。大多数情况下这是可行的,因为您可以控制回调的输入和附加的气体量。如果回调失败,则类似于在异常处理代码中出现异常。
另请注意,您可以确保 callback
已正确安排,并在 contractA.run()
中附加了足够的气体。如果不是这种情况,例如你没有足够的 gas 附加到 run
,回调和其他承诺的调度将失败并且 run
更改的整个状态被回滚。
但是一旦 run
完成,来自 run
的状态更改将被提交并且必须仔细处理 callback
。
我们在 lockup
合约中有几个地方允许回调失败:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L7-L24
还有大部分回调不会失败的地方:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L28-L61
指出在某些情况下合约不想依赖其他合约的稳定性,例如当流量为 A --> B --> A --> B
时。在这种情况下 B
无法将回调附加到提供给 A
的资源。对于这些场景,我们正在讨论添加一个特定构造的可能性,该构造是一个原子并且一旦被删除就会有一个解析回调。我们称它为 Safe
:https://github.com/nearprotocol/NEPs/pull/26
编辑
What if contractB.run
fails and I will like to update the state in contractA
to rollback changes from contractA.run
?
在这种情况下,仍然会调用 contractA.callback()
,但它的依赖项 contractB.run
具有 PromiseResult::Failed
。
因此 callback()
可以修改 contractA
的状态以恢复更改。
例如,锁定合约实现的回调处理从质押池合约中的提款:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/foundation_callbacks.rs#L143-L185
如果我们调整名称以匹配示例:
锁定合约(contractA
)尝试从质押池(contractB
)中提取资金(run()
),但由于最近未质押,资金可能仍处于锁定状态,所以取款失败(contractB.run()
失败)。
调用回调 (contractA.callback()
) 并检查承诺是否成功 (contractB.run
)。由于提现失败,callback reverts the state back to the original(恢复状态)
实际上,它稍微复杂一些,因为实际序列是 A.withdraw_all -> B.get_amount -> A.on_amount_for_withdraw -> B.withdraw(amount) -> A.on_withdraw
如何在合约之间的异步调用链中捕获并处理异常?
假设我的事务发起了以下调用:
contractA.run()
-> do changes in contractA
-> calls contractB.run()
-> do changes in contractB
-> then calls another method on contractA: contractA.callback()
* callback() crashes
在 Promise 出现异常后,NEAR 不会回滚过去 Promise 中发生的更改。我也没有在 near-sdk.
中看到任何处理异常的方法一个想法是 return 错误而不是抛出异常并创建一堆私有函数来更新错误值和添加/释放互斥体后的状态。然而,这并不能解决有时我们无法控制的问题,例如在外部智能合约中(例如,如果 contractB.do
会在上面的示例中出现恐慌)。
捕获异常的唯一方法是对生成异常的承诺进行回调。
在解释的场景中,contractA.callback()
应该不会崩溃。您需要足够仔细地构建合约,以避免回调失败。大多数情况下这是可行的,因为您可以控制回调的输入和附加的气体量。如果回调失败,则类似于在异常处理代码中出现异常。
另请注意,您可以确保 callback
已正确安排,并在 contractA.run()
中附加了足够的气体。如果不是这种情况,例如你没有足够的 gas 附加到 run
,回调和其他承诺的调度将失败并且 run
更改的整个状态被回滚。
但是一旦 run
完成,来自 run
的状态更改将被提交并且必须仔细处理 callback
。
我们在 lockup
合约中有几个地方允许回调失败:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L7-L24
还有大部分回调不会失败的地方:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L28-L61
指出在某些情况下合约不想依赖其他合约的稳定性,例如当流量为 A --> B --> A --> B
时。在这种情况下 B
无法将回调附加到提供给 A
的资源。对于这些场景,我们正在讨论添加一个特定构造的可能性,该构造是一个原子并且一旦被删除就会有一个解析回调。我们称它为 Safe
:https://github.com/nearprotocol/NEPs/pull/26
编辑
What if
contractB.run
fails and I will like to update the state incontractA
to rollback changes fromcontractA.run
?
在这种情况下,仍然会调用 contractA.callback()
,但它的依赖项 contractB.run
具有 PromiseResult::Failed
。
因此 callback()
可以修改 contractA
的状态以恢复更改。
例如,锁定合约实现的回调处理从质押池合约中的提款:https://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/foundation_callbacks.rs#L143-L185
如果我们调整名称以匹配示例:
锁定合约(contractA
)尝试从质押池(contractB
)中提取资金(run()
),但由于最近未质押,资金可能仍处于锁定状态,所以取款失败(contractB.run()
失败)。
调用回调 (contractA.callback()
) 并检查承诺是否成功 (contractB.run
)。由于提现失败,callback reverts the state back to the original(恢复状态)
实际上,它稍微复杂一些,因为实际序列是 A.withdraw_all -> B.get_amount -> A.on_amount_for_withdraw -> B.withdraw(amount) -> A.on_withdraw