一、啰嗦兩句
昨天講了智能合約的一生,那丫長啥樣啊?今兒我就跟各位嘮叨嘮叨。
二、一個簡單的智能合約
下面這段就是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的編譯器編譯成指令碼,這部分就復雜咯,后面咱們講編譯器的時候詳細的說,編譯器這塊還是挺復雜的,面向代碼作者拜一拜。。。
三、小結
智能合約好寫,很容易理解,但是從頭擼到底才能透徹的知道了解它的運行原理,擼的過程略過了很多東西,聰明的你一定很好奇吧,別着急,咱們下回書,再說。