共计 2373 个字符,预计需要花费 6 分钟才能阅读完成。
上一篇《Neo 编译器》中说明了 Neo 编译器是怎么把 CIL 转成 neo 虚拟机的 opcode,那么 vm 虚拟机又是怎么处理这些代码的,这篇文章我们看一下虚拟机的代码。
框架
虚拟机所处的位置
在框架图中,我们可以看出 Virtual Machine 有以下作用
读取 Opcode(smart contract),在 Execution Engine 中执行
Execution Engine 可以进行逻辑运算
Interop Service 可以调用 External Data
系统调用(OP_SYSCALL)可以访问区块链账本的信息
下面我们先看一下虚拟机如何读取 Opcode。
VM 对象关系
下面展示的图不是 UML, UML 太麻烦,还是脑图比较符合思维逻辑的发展。
关键的几个对象
Execution Engine:执行引擎
Execution Context:执行上下文
Stack Item:堆栈的一条数据
Crypto:C# 的加密库
执行引擎
IScriptTable 里面存贮了 AppCall 命令可以调用的其他 contract 的代码,这一块需要研究一下区块链的实现,这个以后再仔细研究。
InteropService 专门用来响应 SYSCALL,具体有哪些是系统调用,用来干什么的,后面通过例子再来说明。
InvocationStack 是调用栈,传入参数,调用其他合约都会有一个新的调用栈
EvaluationStack 是计算栈,用来执行操作
AltStack 是备用栈,计算栈算出的中间结果可以保存在备用栈
执行上下文
执行上下文
每个变量都蛮好理解的,重点是下面看看怎么用的。
vm 执行流程
vm 代码执行流程
构造,此时可以传入 script container,script table,后面我们看看在区块链上这些都是从哪里来的,这里只专注于 vm 的执行流程,暂且不深究了。
加载.avm,avm 是编译器编译出来的一串数字,通过 engine.LoadScript 可以加载。
execute 开始执行, 下面看一下代码
public void Execute()
{
State &= ~VMState.BREAK;
while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT) && !State.HasFlag(VMState.BREAK))
StepInto();
}
public void StepInto()
{
if (InvocationStack.Count == 0) State |= VMState.HALT;
if (State.HasFlag(VMState.HALT) || State.HasFlag(VMState.FAULT)) return;
OpCode opcode = CurrentContext.InstructionPointer >= CurrentContext.Script.Length ? OpCode.RET : (OpCode)CurrentContext.OpReader.ReadByte();
try
{
ExecuteOp(opcode, CurrentContext);
}
catch
{
State |= VMState.FAULT;
}
}
看一下这行代码,OpCode opcode = CurrentContext.InstructionPointer >= CurrentContext.Script.Length ? OpCode.RET : (OpCode)CurrentContext.OpReader.ReadByte();
代码执行完了以后,插入 OpCode.RET
如果不是 RET,则 read 一个字节的 opcode
ExecuteOp 函数就是具体的执行 OpCode 的语义,我们通过一个例子来说明
具体的一个例子
还是上次的那个代码
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
public class Sum : SmartContract
{
public static int Main(int a, int b)
{
return a + b;
}
}
测试虚拟机的代码
using System;
using System.IO;
using System.Linq;
using Neo;
using Neo.VM;
using Neo.Cryptography;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var engine = new ExecutionEngine(null, Crypto.Default);
engine.LoadScript(File.ReadAllBytes(@”C:\……\Test1.avm”));
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitPush(4); // 对应形参 b
sb.EmitPush(3); // 对应形参 a
engine.LoadScript(sb.ToArray());
}
engine.Execute(); // 开始执行
var result = engine.EvaluationStack.Peek().GetBigInteger(); // 在这里设置返回值
Console.WriteLine($” 执行结果 {result}”);
Console.ReadLine();
}
}
}
执行的具体过程
生成的代码太长了,需要有点耐心才能看完,如果图片不清晰,可以去代码仓库下载 pdf
具体的执行过程
总结
文章只是过了一下一个简单的代码,后面我们需要研究一下系统调用和访问外部存贮,智能合约之间互相调用的情况。
作者:沈寅原文链接:https://www.jianshu.com/p/b7a…