NEO從入門到開窗(2) - 智能合約的面相


一、啰嗦兩句

昨天講了智能合約的一生,那丫長啥樣啊?今兒我就跟各位嘮叨嘮叨。

 

二、一個簡單的智能合約

下面這段就是NEO實例源碼里的一個,干撒用的?聰明的你一眼兒就看出來了吧,就是一個所謂域名合約的增刪改查。有幾點我嘮叨兩句

using Neo.SmartContract.Framework.Services.Neo;

namespace Neo.SmartContract
{
    public class Domain : Framework.SmartContract
    {
        public static object Main(string operation, params object[] args)
        {
            switch (operation)
            {
                case "query":
                    return Query((string)args[0]);
                case "register":
                    return Register((string)args[0], (byte[])args[1]);
                case "transfer":
                    return Transfer((string)args[0], (byte[])args[1]);
                case "delete":
                    return Delete((string)args[0]);
                default:
                    return false;
            }
        }

        private static byte[] Query(string domain)
        {
            return Storage.Get(Storage.CurrentContext, domain);
        }

        private static bool Register(string domain, byte[] owner)
        {
            if (!Runtime.CheckWitness(owner)) return false;
            byte[] value = Storage.Get(Storage.CurrentContext, domain);
            if (value != null) return false;
            Storage.Put(Storage.CurrentContext, domain, owner);
            return true;
        }

        private static bool Transfer(string domain, byte[] to)
        {
            if (!Runtime.CheckWitness(to)) return false;
            byte[] from = Storage.Get(Storage.CurrentContext, domain);
            if (from == null) return false;
            if (!Runtime.CheckWitness(from)) return false;
            Storage.Put(Storage.CurrentContext, domain, to);
            return true;
        }

        private static bool Delete(string domain)
        {
            byte[] owner = Storage.Get(Storage.CurrentContext, domain);
            if (owner == null) return false;
            if (!Runtime.CheckWitness(owner)) return false;
            Storage.Delete(Storage.CurrentContext, domain);
            return true;
        }
    }
}

1. 上一節講了,一個智能合約就是繼承自SmartContract的類,Main函數啊,方法啊,全得是靜態的,因為NEO的編譯器就認靜態的。

2. Main就是智能合約的入口,調用的時候會穿些參數,第一個就是opeartion,根據這個來判斷是啥操作,這點和Fabric很像,就一個init一個invoke,Etherenum不是這樣的。

3. 邏輯都能看明白,這個Storage是個啥玩意,看上去像個存儲,在哪存儲?這不就是serverless嗎?ok,咱就以此為突破,重點關注一下這個Storage。

源碼說話。這個Storage是NEO工具包(SmartContract那個類一個項目下的)里的一個類,我們看下源碼。

using System.Numerics;

namespace Neo.SmartContract.Framework.Services.Neo
{
    public static class Storage
    {
        public static extern StorageContext CurrentContext
        {
            [Syscall("Neo.Storage.GetContext")]
            get;
        }

        [Syscall("Neo.Storage.Get")]
        public static extern byte[] Get(StorageContext context, byte[] key);
    }
}

我把其他方法都刪了,留一個Get方法,示意一下。看到這,是不是有點懵逼,SysCall什么鬼,這種寫法是不是似曾相識?是不是和dllimport差不多?其實我琢磨着作者寫的時候也有這么個意圖,就說啊,這是一系統調用啊,調用咱系統的東西。那到底這玩意兒咋個原理,咱們就得看NEO的編譯器源碼了。上一節咱提了,NEO的編譯器就是根據規則把對應的代碼翻譯成指令碼,編譯器這部分咱后面也要說,找源碼的過程比較瑣碎,不貼代碼了,這里先理解成編譯器把他編譯成OpCode.SYSCALL Neo.Storage.Get,就是系統調用Neo.Storage.Get。下面的問題就是NEO的虛擬機該怎么解釋這個指令了,我們再去NEO.VM項目里找結果。

private readonly InteropService service;

            case
OpCode.SYSCALL: if (!service.Invoke(Encoding.ASCII.GetString(context.OpReader.ReadVarBytes(252)), this)) State |= VMState.FAULT; break;

再來看看Interopservice里Invoke都干了些啥,也是刪除了部分代碼。

using System;
using System.Collections.Generic;

namespace Neo.VM
{
    public class InteropService
    {
        private Dictionary<string, Func<ExecutionEngine, bool>> dictionary = new Dictionary<string, Func<ExecutionEngine, bool>>();

        public InteropService()
        {
            Register("System.ExecutionEngine.GetScriptContainer", GetScriptContainer);
            Register("System.ExecutionEngine.GetExecutingScriptHash", GetExecutingScriptHash);
            Register("System.ExecutionEngine.GetCallingScriptHash", GetCallingScriptHash);
            Register("System.ExecutionEngine.GetEntryScriptHash", GetEntryScriptHash);
        }

        protected void Register(string method, Func<ExecutionEngine, bool> handler)
        {
            dictionary[method] = handler;
        }

        internal bool Invoke(string method, ExecutionEngine engine)
        {
            if (!dictionary.ContainsKey(method)) return false;
            return dictionary[method](engine);
        }
    }
}

看起來InteropService里保留了一個字段,根據不同的方法名,注冊不同的執行方法,我們要找的Neo.Storage.Get這個方法key是在那里注冊的呢?搜。  

namespace Neo.SmartContract
{
    public class StateReader : InteropService
    {
        public event EventHandler<NotifyEventArgs> Notify;
        public event EventHandler<LogEventArgs> Log;

        public static readonly StateReader Default = new StateReader();

        public StateReader()
        {
            Register("Neo.Storage.Get", Storage_Get);
        }

       protected virtual bool Storage_Get(ExecutionEngine engine)
        {
            StorageContext context = engine.EvaluationStack.Pop().GetInterface<StorageContext>();
            ContractState contract = Blockchain.Default.GetContract(context.ScriptHash);
            if (contract == null) return false;
            if (!contract.HasStorage) return false;
            byte[] key = engine.EvaluationStack.Pop().GetByteArray();
            StorageItem item = Blockchain.Default.GetStorageItem(new StorageKey
            {
                ScriptHash = context.ScriptHash,
                Key = key
            });
            engine.EvaluationStack.Push(item?.Value ?? new byte[0]);
            return true;
        }
    }
}

注意到Blockchain.Default.GetstorageItem()這個方法了吧,虛擬機執行器把鏈上的數據取出來了,這個Blockchain的實現是leveldb這個咱也后面再講。

好啦,到現在這一圈可以理解智能合約的執行過程了吧。聰明的你也一定發現了,你看的懂的那些邏輯,也會被NEO的編譯器編譯成指令碼,這部分就復雜咯,后面咱們講編譯器的時候詳細的說,編譯器這塊還是挺復雜的,面向代碼作者拜一拜。。。

 

三、小結

智能合約好寫,很容易理解,但是從頭擼到底才能透徹的知道了解它的運行原理,擼的過程略過了很多東西,聰明的你一定很好奇吧,別着急,咱們下回書,再說。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM