零点课堂 | 如何设计区块链中的异常处理机制(2)
异常类型
常见将异常分为如下几大类:
Error,程序无法处理的异常,表示运行中出现较严重的问题。大多数错误与代码编写者的操作无关,而是代码运行时出现的问题,并往往导致程序无法恢复进行。如常见的内存溢出,内存不够,栈溢出等。
Exception, 程序本身可以处理的异常,有细分为如下两类:
RuntimeException
运行时异常
程序中可以选择捕获这些异常处理,也可以选择不捕获处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽量避免此类异常的发生。出现运行时异常后,如果没有捕获处理此异常,系统会把异常一层层往上抛,一直到最上层,如果仍未被捕获上,则会结束该段程序执行。常见的类型有:空指针异常,数组越界,合约不存在,除数为零等。
NonRuntimeException
运行时异常以为的所有异常
如其他合约中定义的异常,以及用户自己定义的一些异常。这些异常一般通过显示的调用throw指令进行抛出。
区块链中的异常处理特性
相比起单语言,一个公链往往会有多个不同语言版本的实现,确保不同语言不同平台下的对异常触发确定性和一致性变得尤为重要。
在前面的介绍中,Exception分为两类。一类,VM在内部之时遇到的运行时异常。另外一类是非运行时异常,常见的是用户手动通过throw指令抛出的异常。该类异常的都是有确定的执行路径,属于确定的。
在NEO3-VM中,主要处理非运行时的异常,即用户通过throw指令,显示触发的异常。并在内部抽象了一个可捕获的异常抽象类:CatchableException, 未来可根据需要拓展不同类型的异常。
Neo3-VM的异常机制实现原理
在Neo3-VM中,采用如下机制进行异常处理。新增了TryContext上下文,记录当前Try信息,包括了try-catch的各段的起始位置,以及当前TryContext上下文所处状态(总共三类状态:try监控状态,catch捕获阶段,finally扫尾阶段)。并在当前执行上下文 ExecutionContext中,添加TryStack栈,记录当前执行过程中,遇到的try-catch信息,即支持try-catch嵌套。
当一个异常发生时,会从调用栈栈顶的执行上下文的Try栈进行查找,如果存在某个try-catch能够捕获该异常,则进入该try-catch的catch段或finally端执行。否则退栈处理,直至遇到对应的try-catch或结束VM执行。
若异常本身发生在catch段
会先去执行finally段,再重新向外抛出该异常,并退出当前trycontext上下文。
若异常发生在finally段
会直接抛出该异常,并退出当前trycontext上下文。