如何判断是否上链成功?

如何判断CITA上什么样的交易是有问题的呢?从目前通过SDK调用上分三部分:
一部分不会产生交易hash,直接返回-32000~32700的json-rpc错误请求;
一部分链上交易回执中的erroMsg中有消息;
另一部分例如系统合约调用需要通过Log中的Topic判断。那针对其他普通合约调用,如何才算是成功的呢,只需要交易回执没有errorMsg,还是需要根据业务逻辑对Topic判断?

这里有三种情况:

  1. 没有交易 hash, 这种是连网络都不通,那么直接可以判断不能上链。

  2. 有交易 hash, 说明已经进交易池。需要通过 ValidUntilBlock 超时 与 Receipt 共同判断是否能上链。

  3. Topic 是需要合约设计 Event 的,这个是业务层的设计。 」


新问题:

1、像验证签名不过和重复交易等都是属于第一种情况?

2、有交易Hash,具体怎么根据ValidUntilBlock和Receipt判断是否成功呢,成功的标识是Receipt中没有错误信息?

3、就是含有Topic的话最终是否成功是需要业务自己根据Topic来判断,不能从SDK层做出判断哈?

调用合约的话,是不是能够根据hash查询到receipt,且Receipt没有errormsg,就可以认为是成功的 ?

你总结的大体都是对的。

这其实跟调用传统的jsonrpc服务很类似。
一次jsonrpc调用都要经过前置的一个http server加上后台的若干微服务,具体经过哪些微服务要看具体是什么业务。走到路径上不同节点出错,就会返回相应的报错。

在cita里,就发交易这个场景来说。按照顺序,要经过jsonrpc,auth,共识,executor这几个微服务。其中共识模块不会给用户返回错误信息,所以可以忽略共识。
但是以共识为界,可以将这个过程分为两个阶段。

  1. 交易经过jsonrpc,然后到auth进行合法性检查,成功后进入交易池。
  2. 共识从交易池取交易组装成block,共识之后发给executor执行。

因为第2个阶段是异步执行的,所以发送交易的jsonrpc调用到第一阶段就返回了。

  1. jsonrpc微服务,其实就是前置的http server加上对json格式的解析。如果json格式不对,或者里面的数据格式有问题(超出取值范围/该十进制的传十六进制)。会在这一步就直接返回错误。这里当然也包含jsonrpc协议本身定义的错误码,就是你说的-32000~32700。
  2. auth微服务。检查交易的每一个字段的合法性,包括签名,重复交易等等。如果交易合法,进入交易池,给用户返回交易hash,否则返回错误码。

我们的手册上有专门一页描述jsonrpc返回的错误码的。


这些错误码是包含了前述两个微服务的。因为前面说过,第一阶段这两个微服务是同步调用的,用户不看具体错误码是感知不到具体哪个微服务报错的。

第二个阶段是异步执行的,交易执行过程中出错没法马上通知用户,所以只能通过查询receipt来看了。
只要是上链的交易,不管执行成功还是失败,一定会有对应的receipt。
receipt里可能出现的错误码在手册上有列出来

Log这些东西完全是业务层的。不用log,通过合约里把相应的信息存在变量里,然后提供get接口,完全可以实现同样的功能。

ValidUntilBlock 是用于非常特殊的一种场景。
就是第一阶段成功返回交易hash,但是一直查不到receipt。
前面说过正常情况下,不管交易执行成功还是失败,都会有receipt。
所以这里说的特殊场景有点像是交易失踪了,活不见人,死不见尸。
具体原因有很多,就不展开了。只要知道确实有这么一类特殊情况,当然其出现的概率非常低。

这时等到链的高度到达ValidUntilBlock,如果还查不到receipt,就可以确认交易没上链。
所以ValidUntilBlock的范围是当前区块高度到当前区块高度 +100 之间,但是具体加多少,是交易发送者可以自己定的。
万一遇到这种特殊情况,不想等太久,那就加少一点。
当然也不能太少了,如果还没发出去就过期了,就得重新构造交易。

之前在进行节点升级降级操作的时候,当不是管理员发送的交易,最终能够得到Receipt且无错误信息。
而查看系统合约中,升级合约如下:
/// @notice Approve the new node
/// The modifier of oneOperate will be deprecated!
/// @param _node The node to be approved
/// @return true if successed, otherwise false
function approveNode(address _node)
public
onlyAdmin
oneOperate
onlyClose(_node)
hasPermission(builtInPermissions[15])
returns (bool)
{
status[_node] = NodeStatus.Start;
block_op[block.number] = true;
nodes.push(_node);
emit ApproveNode(_node);
return true;
}
对是否是管理员的判断为:
modifier onlyAdmin {
if (admin.isAdmin(msg.sender))
_;
else return;
}


其他权限或者用户判断的方式为:
modifier onlyStart(address _node) {
if (NodeStatus.Start == status[_node])
_;
else {
emit ErrorLog(ErrorType.NotStart, “node does not start”);
return;
}
}
或者:
modifier onlySeller() {
require(
msg.sender == seller,
“Only seller can call this.”
);
_;
}
这样的话就可以知道是否错误。

————————————————————————————

而在链升级降级这个系统合约操作的时候,最终成功需要根据Topic是否含有节点地址来判断。为什么不直接在判断不是admin的时候就提示错误呢?
请问这里的admin判断方式有什么特别的考虑吗?

@left 知道为什么这么设计吗?

实际上 isAdmin 函数是有个event的

    function isAdmin(address _account)
        public
        returns (bool)
    {
        if (_account == admin) {
            return true; 
        } 
        emit ErrorLog(ErrorType.NotAdmin, "Not the admin account");
    }

至于你说的为什么不直接require,返回 revert 错误,是因为你可以看 error.sol

enum ErrorType {
        NotAdmin,
        OutOfBaseLimit,
        OutOfBlockLimit,
        NoParentChain,
        NoSideChain,
        NotOneOperate,
        NotClose,
        NotStart,
        NotReady
    }

你看代码也可以看到每个操作其实都有很多 modifier,如果每个都是 require,那么错误类型都是 revert,当然判断的时候是可以直接在 errorMsg 那里判断,但是具体错误类型无法知晓。

至于这个是否只对 errorMsg 还是多个节点地址判断,其实都是链下的逻辑,多一个逻辑还是多一块逻辑我觉得都还好,一行代码和一个函数的区别。但是对于链上错误的debug就比较麻烦,这样是把链上debug需要的一些信息转移到链下