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的编译器编译成指令码,这部分就复杂咯,后面咱们讲编译器的时候详细的说,编译器这块还是挺复杂的,面向代码作者拜一拜。。。

 

三、小结

智能合约好写,很容易理解,但是从头撸到底才能透彻的知道了解它的运行原理,撸的过程略过了很多东西,聪明的你一定很好奇吧,别着急,咱们下回书,再说。

 

posted @ 2018-03-21 23:28  Dexter Di  阅读(1016)  评论(2编辑  收藏  举报