Neo 虛擬機器

NEOGO發表於2018-12-27

上一篇《Neo 編譯器》中說明了Neo編譯器是怎麼把CIL轉成neo虛擬機器的opcode,那麼vm虛擬機器又是怎麼處理這些程式碼的,這篇文章我們看一下虛擬機器的程式碼。

框架

虛擬機器所處的位置

在框架圖中,我們可以看出Virtual Machine有以下作用

  1. 讀取Opcode(smart contract),在Execution Engine中執行
  2. Execution Engine可以進行邏輯運算
  3. Interop Service可以呼叫External Data
  4. 系統呼叫(OP_SYSCALL)可以訪問區塊鏈賬本的資訊

下面我們先看一下虛擬機器如何讀取Opcode。

VM物件關係

下面展示的圖不是UML, UML太麻煩,還是腦圖比較符合思維邏輯的發展。

關鍵的幾個物件
  1. Execution Engine:執行引擎
  2. Execution Context:執行上下文
  3. Stack Item:堆疊的一條資料
  4. Crypto:C#的加密庫

執行引擎

  1. IScriptTable裡面存貯了AppCall命令可以呼叫的其他contract的程式碼,這一塊需要研究一下區塊鏈的實現,這個以後再仔細研究。
  2. InteropService專門用來響應SYSCALL,具體有哪些是系統呼叫,用來幹什麼的,後面通過例子再來說明。
  3. InvocationStack是呼叫棧,傳入引數,呼叫其他合約都會有一個新的呼叫棧
  4. EvaluationStack是計算棧,用來執行操作
  5. AltStack是備用棧,計算棧算出的中間結果可以儲存在備用棧

執行上下文

執行上下文

每個變數都蠻好理解的,重點是下面看看怎麼用的。

vm執行流程

vm程式碼執行流程

  1. 構造,此時可以傳入script container,script table,後面我們看看在區塊鏈上這些都是從哪裡來的,這裡只專注於vm的執行流程,暫且不深究了。
  2. 2.載入.avm,avm是編譯器編譯出來的一串數字,通過engine.LoadScript可以載入。
  3. 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();

  1. 程式碼執行完了以後,插入OpCode.RET
  2. 如果不是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

具體的執行過程

總結

文章只是過了一下一個簡單的程式碼,後面我們需要研究一下系統呼叫和訪問外部存貯,智慧合約之間互相呼叫的情況。

作者:沈寅
原文連結:www.jianshu.com/p/b7a50b7e0…


相關文章