NEO C# 合约编译器原理解析

NEO C# 合约编译器原理解析

NEO合约编译过程牵涉到几个项目

  1. neo-compiler下的neon项目负责code码转换
  2. neo-devpack-dotnetx下的Neo.SmartContract.Framework负责公共接口定义
  3. neo项目实现了framework中的接口

原理

c#的版本很多,从framework2.0到core2.3版本,语法差异很大,但是底层对应MSIL字节码没有变化,Neo的原理是先使用对应的编译器生成MSIL字节码,再把MSIL字节码转换成NEO vm的code码序列。这样做的好处在与利用了C#现有的语法成果,不必自己在设计一门语言,减少了合约编写的门槛。

大道不过三两行,说穿不值一文钱。下面我会通过一个完整的例子来说明这个流程,希望能藉此帮助更多人了解其中的原理细节,写出效率更好,费用更低的合约。

代码是在github上面找的,NEO-NEP5.1是NEP5的一个token,包含常用的元素,字段,事件与函数。具有常见的数据存储,合约调用及日志信息功能。

代码生成

c# -> MSIL

源码大致结构:实现细节摘除了,需要的请点击上面的链接

using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using System;
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract
{
    public class ICO_Template : Framework.SmartContract
    {
        public static string Name() => "GagaPay network token";
        public static string Symbol() => "GTA";
        public static readonly byte[] Owner = "Abdeg1wHpSrfjNzH5edGTabi5jdD9dvncX".ToScriptHash();
        public static byte Decimals() => 8;
        private const ulong factor = 100000000; //decided by Decimals()
        private const ulong total_amount = 1000000000 * factor; //token amount

        [DisplayName("transfer")]
        public static event Action<byte[], byte[], BigInteger> Transferred;

        public static Object Main(string operation, params object[] args)

        public static bool Deploy()

        public static BigInteger TotalSupply()

        public static BigInteger Allowance(byte[] from, byte[] to)

        public static bool Approve(byte[] originator, byte[] to, BigInteger amount)

        public static bool TransferFrom(byte[] originator, byte[] from, byte[] to, BigInteger amountToSend)

        public static bool Transfer(byte[] from, byte[] to, BigInteger value, bool transferFrom)

        public static BigInteger BalanceOf(byte[] address)
    }
}

编译

    dotnet restore
    dotnet publish

编译完成后使用工具查看dll的类布局,其中字段还原没有问题,多了个类构造和构造函数,还有event对应出来的两个add/remove方法,后来在转换过程中都需要清除掉的.事实上在neo中event的更多的只是起到了标识的作用。具体的MSIL CODE太多就不贴上来了,下面提到哪里就贴到哪里.如果需要完整的文件,这里推荐一个常用工具ildasm,用来查看dll的语言信息十分方便。

___[MOD] C:\Users\10844\Desktop\neo\NEP-5.1\NEP-5.1\bin\Debug\netcoreapp2.1\NEP-5.1.dll
   |      M A N I F E S T
   |___[NSP] Neo.SmartContract
   |   |___[CLS] Neo.SmartContract.ICO_Template
   |   |   |     .class public auto ansi beforefieldinit 
   |   |   |      extends [Neo.SmartContract.Framework]Neo.SmartContract.Framework.SmartContract 
   |   |   |___[STF] Owner : public static initonly uint8[]
   |   |   |___[STF] Transferred : private static class [System.Runtime]System.Action`3<uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger>
   |   |   |___[STF] factor : private static literal uint64
   |   |   |___[STF] total_amount : private static literal uint64
   |   |   |___[STM] .cctor : void()
   |   |   |___[MET] .ctor : void()
   |   |   |___[STM] Allowance : valuetype [System.Runtime.Numerics]System.Numerics.BigInteger(uint8[],uint8[])
   |   |   |___[STM] Approve : bool(uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger)
   |   |   |___[STM] BalanceOf : valuetype [System.Runtime.Numerics]System.Numerics.BigInteger(uint8[])
   |   |   |___[STM] Decimals : uint8()
   |   |   |___[STM] Deploy : bool()
   |   |   |___[STM] Main : object(string,object[])
   |   |   |___[STM] Name : string()
   |   |   |___[STM] NotifyErrorAndReturn0 : int32(string)
   |   |   |___[STM] NotifyErrorAndReturnFalse : bool(string)
   |   |   |___[STM] Symbol : string()
   |   |   |___[STM] TotalSupply : valuetype [System.Runtime.Numerics]System.Numerics.BigInteger()
   |   |   |___[STM] Transfer : bool(uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger,bool)
   |   |   |___[STM] TransferFrom : bool(uint8[],uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger)
   |   |   |     add_Transferred : void(class [System.Runtime]System.Action`3<uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger>)
   |   |   |     remove_Transferred : void(class [System.Runtime]System.Action`3<uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger>)
   |   |   |___[EVT] Transferred : class [System.Runtime]System.Action`3<uint8[],uint8[],valuetype [System.Runtime.Numerics]System.Numerics.BigInteger>
   |   |
   |

MSIL -> 合约字节码

MSIL转换合约字节码工具是在neo-compiler/neon中定义的,转换命令为

    dotnet .\neon.dll NEP.dll

这里可能会报错

LoadModule Error:System.Exception: can't parese event type from:System.Action`3<System.Byte[],System.Byte[],System.Numerics.BigInteger>.maybe it is System.Action<xxx> which is defined in mscorlib.dll,copy this dll in.

这个错误估计是底下的库无法正确处理Action导致,这里手动定义下事件,改变下原来Transfer的定义,熟悉C#语法的人应该知道这两种写法几乎是等同的。

    //原来
    [DisplayName("transfer")]
    public static event Action<byte[], byte[], BigInteger> Transferred;

    //修改为
    public delegate void transferDelegete(byte[] s1, byte[] s2, BigInteger  num);
    [DisplayName("transfer")]
    public static event transferDelegete Transferred;

修改完成后重新执行命令‘dotnet .\neon.dll NEP.dll’。看到如下字样即是成功的转换了类库,此时在运行目录下可以看到一个NEP.avm和一个NEP.abi的文件,前者包含了运行所需的字节码,后者仅仅描述了方法和事件信息。

    Neo.Compiler.MSIL console app v2.3.0.8
    Find entrypoint:System.Object Neo.SmartContract.ICO_Template::Main(System.String,System.Object[])
    convert succ
    gen abi succ
    write:NEP.avm
    write:NEP.abi.json
    SUCC

到这neo字节码生成完毕,然后就按官方的方法把这个vm文件发布出去。

代码调用

合约代码入口就是文件的main函数,通常是根据传入的函数名称判断调用到对应的工作函数。下面会通过两个具体函数的执行过程,通过对比三种代码来说明这个编译执行的过程。

部署合约函数流程

入口跳转

c#

    if (operation == "deploy") return Deploy();

MSIL

    IL_007a:  nop
    IL_007b:  ldarg.0
    IL_007c:  ldstr      "deploy"
    IL_0081:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                    string)
    IL_0086:  stloc.s    V_6
    IL_0088:  ldloc.s    V_6
    IL_008a:  brfalse.s  IL_009c
    IL_008c:  call       bool Neo.SmartContract.ICO_Template::Deploy()
    IL_0091:  box        [mscorlib]System.Boolean
    IL_0096:  stloc.2

VM OPCODE

    012D NOP             []
    012E FROMALTSTACK    []
    012F DUP             []
    0130 TOALTSTACK      []
    0131 PUSH0           []
    0132 PICKITEM        []
    0133 6               [deploy]
    013A EQUAL           []
    013B FROMALTSTACK    []
    013C DUP             []
    013D TOALTSTACK      []
    013E PUSH8           []
    013F PUSH2           []
    0140 ROLL            []
    0141 SETITEM         []
    0142 FROMALTSTACK    []
    0143 DUP             []
    0144 TOALTSTACK      []
    0145 PUSH8           []
    0146 PICKITEM        []
    0147 JMPIFNOT        [0013]
    014A NOP             []
    014B CALL_I          [0100BD04]
    0150 FROMALTSTACK    []
    0151 DUP             []
    0152 TOALTSTACK      []
    0153 PUSH4           []
    0154 PUSH2           []
    0155 ROLL            []
    0156 SETITEM         []

其中主要看两条指令,一个是IL_0081/013A,一个是IL_008c/014B,前者就是判断参数是不是depoly,后者是判断成功后进行函数跳转,调用需要执行的函数。

调用部署函数

c#

    if (!Runtime.CheckWitness(Owner)) //ensure that it is the owner calling this method
        return NotifyErrorAndReturnFalse("You are not the Owner of this Smart Contract");

    byte[] total_supply = Storage.Get(Storage.CurrentContext, "totalSupply");

    if (total_supply.Length != 0)
        return NotifyErrorAndReturnFalse("Looks like this method has been allready used");

    Storage.Put(Storage.CurrentContext, Owner, total_amount);
    Storage.Put(Storage.CurrentContext, "totalSupply", total_amount);
    Transferred(null, Owner, total_amount);
    return true;

MSIL

    IL_0000:  nop
    IL_0001:  ldsfld     uint8[] Neo.SmartContract.ICO_Template::Owner
    IL_0006:  call       bool [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Runtime::CheckWitness(uint8[])
    IL_000b:  ldc.i4.0
    IL_000c:  ceq
    IL_000e:  stloc.1
    IL_000f:  ldloc.1
    IL_0010:  brfalse.s  IL_0022
    IL_0012:  ldstr      "You are not the Owner of this Smart Contract"
    IL_0017:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_001c:  stloc.2
    IL_001d:  br         IL_00a7
    IL_0022:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_0027:  ldstr      "totalSupply"
    IL_002c:  call       uint8[] [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Get(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                    string)
    IL_0031:  stloc.0
    IL_0032:  ldloc.0
    IL_0033:  ldlen
    IL_0034:  ldc.i4.0
    IL_0035:  cgt.un
    IL_0037:  stloc.3
    IL_0038:  ldloc.3
    IL_0039:  brfalse.s  IL_0048
    IL_003b:  ldstr      "Looks like this method has been allready used"
    IL_0040:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_0045:  stloc.2
    IL_0046:  br.s       IL_00a7
    IL_0048:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_004d:  ldsfld     uint8[] Neo.SmartContract.ICO_Template::Owner
    IL_0052:  ldc.i8     0x16345785d8a0000
    IL_005b:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(uint64)
    IL_0060:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                uint8[],
                                                                                                                valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_0065:  nop
    IL_0066:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_006b:  ldstr      "totalSupply"
    IL_0070:  ldc.i8     0x16345785d8a0000
    IL_0079:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(uint64)
    IL_007e:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                string,
                                                                                                                valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_0083:  nop
    IL_0084:  ldsfld     class Neo.SmartContract.ICO_Template/helo Neo.SmartContract.ICO_Template::Transferred
    IL_0089:  ldnull
    IL_008a:  ldsfld     uint8[] Neo.SmartContract.ICO_Template::Owner
    IL_008f:  ldc.i8     0x16345785d8a0000
    IL_0098:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(uint64)
    IL_009d:  callvirt   instance void Neo.SmartContract.ICO_Template/helo::Invoke(uint8[],
                                                                                    uint8[],
                                                                                    valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_00a2:  nop
    IL_00a3:  ldc.i4.1
    IL_00a4:  stloc.2
    IL_00a5:  br.s       IL_00a7
    IL_00a7:  ldloc.2
    IL_00a8:  ret

VM OPCODE

    060A PUSH4           []//begincode(0)
    060B NEWARRAY        []//(0)
    060C TOALTSTACK      []//(0)
    060D NOP             []
    060E NOP             []
    060F 20              [D9D45BA4DD9497C13A825196A459C6414DA4F020]
    0624 NOP             []
    0625 SYSCALL         [Neo.Runtime.CheckWitness]
    063F PUSH0           []
    0640 NUMEQUAL        []
    0641 FROMALTSTACK    []
    0642 DUP             []
    0643 TOALTSTACK      []
    0644 PUSHT           []
    0645 PUSH2           []
    0646 ROLL            []
    0647 SETITEM         []
    0648 FROMALTSTACK    []
    0649 DUP             []
    064A TOALTSTACK      []
    064B PUSHT           []
    064C PICKITEM        []
    064D JMPIFNOT        [4000]
    0650 44              [You are not the Owner of this Smart Contract]
    067D NOP             []
    067E CALL_I          [01012A0D]
    0683 FROMALTSTACK    []
    0684 DUP             []
    0685 TOALTSTACK      []
    0686 PUSH2           []
    0687 PUSH2           []
    0688 ROLL            []
    0689 SETITEM         []
    068A JMP             [7601]
    068D NOP             []
    068E SYSCALL         [Neo.Storage.GetContext]
    06A6 11              [746F74616C537570706C79]
    06B2 NOP             []
    06B3 SWAP            []//swap 2 param(0)
    06B4 SYSCALL         [Neo.Storage.Get]
    06C5 FROMALTSTACK    []
    06C6 DUP             []
    06C7 TOALTSTACK      []
    06C8 PUSH0           []
    06C9 PUSH2           []
    06CA ROLL            []
    06CB SETITEM         []
    06CC FROMALTSTACK    []
    06CD DUP             []
    06CE TOALTSTACK      []
    06CF PUSH0           []
    06D0 PICKITEM        []
    06D1 ARRAYSIZE       []
    06D2 PUSH0           []
    06D3 GT              []
    06D4 FROMALTSTACK    []
    06D5 DUP             []
    06D6 TOALTSTACK      []
    06D7 PUSH3           []
    06D8 PUSH2           []
    06D9 ROLL            []
    06DA SETITEM         []
    06DB FROMALTSTACK    []
    06DC DUP             []
    06DD TOALTSTACK      []
    06DE PUSH3           []
    06DF PICKITEM        []
    06E0 JMPIFNOT        [4100]
    06E3 45              [Looks like this method has been allready used]
    0711 NOP             []
    0712 CALL_I          [0101960C]
    0717 FROMALTSTACK    []
    0718 DUP             []
    0719 TOALTSTACK      []
    071A PUSH2           []
    071B PUSH2           []
    071C ROLL            []
    071D SETITEM         []
    071E JMP             [E200]
    0721 NOP             []
    0722 SYSCALL         [Neo.Storage.GetContext]
    073A NOP             []
    073B 20              [D9D45BA4DD9497C13A825196A459C6414DA4F020]
    0750 8               [00008A5D78456301]
    0759 NOP             []
    075A PUSH2           []//swap 0 and 2 param(0)
    075B XSWAP           []//(0)
    075C SYSCALL         [Neo.Storage.Put]
    076D NOP             []
    076E NOP             []
    076F SYSCALL         [Neo.Storage.GetContext]
    0787 11              [746F74616C537570706C79]
    0793 8               [00008A5D78456301]
    079C NOP             []
    079D PUSH2           []//swap 0 and 2 param(0)
    079E XSWAP           []//(0)
    079F SYSCALL         [Neo.Storage.Put]
    07B0 NOP             []
    07B1 NOP             []
    07B2 PUSH0           []
    07B3 NOP             []
    07B4 20              [D9D45BA4DD9497C13A825196A459C6414DA4F020]
    07C9 8               [00008A5D78456301]
    07D2 NOP             []
    07D3 PUSH2           []//swap 0 and 2 param(0)
    07D4 XSWAP           []//(0)
    07D5 8               [7472616E73666572]
    07DE PUSH4           []
    07DF PACK            []
    07E0 SYSCALL         [Neo.Runtime.Notify]
    07F4 NOP             []
    07F5 PUSHT           []
    07F6 FROMALTSTACK    []
    07F7 DUP             []
    07F8 TOALTSTACK      []
    07F9 PUSH2           []
    07FA PUSH2           []
    07FB ROLL            []
    07FC SETITEM         []
    07FD JMP             [0300]
    0800 FROMALTSTACK    []
    0801 DUP             []
    0802 TOALTSTACK      []
    0803 PUSH2           []
    0804 PICKITEM        []
    0805 NOP             []
    0806 FROMALTSTACK    []//endcode(0)
    0807 DROP            []//(0)
    0808 RET             []

几个重要的函数调用位置对比:

检查合约所有人

C#:       if (!Runtime.CheckWitness(Owner)) //ensure that it is the owner calling this method
MSIL:        IL_0006:  call       bool [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Runtime::CheckWitness(uint8[])
NEO:        0625 SYSCALL         [Neo.Runtime.CheckWitness]

错误提示:You are not the Owner of this Smart Contract

C#:     return NotifyErrorAndReturnFalse("You are not the Owner of this Smart Contract");
MSIL:   IL_0012:  ldstr      "You are not the Owner of this Smart Contract"
        IL_0017:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
NEO:    0650 44              [You are not the Owner of this Smart Contract]
        067D NOP             []
        067E CALL_I          [01012A0D]

获取当前合约币供应量totalSupply

C#:     byte[] total_supply = Storage.Get(Storage.CurrentContext, "totalSupply");
MSIL:   IL_0022:  call       Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_0027:  ldstr      "totalSupply"
        IL_002c:  call       uint8[] [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Get
NEO:    068E SYSCALL         [Neo.Storage.GetContext]
        06A6 11              [746F74616C537570706C79]
        06B2 NOP             []
        06B3 SWAP            []//swap 2 param(0)
        06B4 SYSCALL         [Neo.Storage.Get]

错误提示:防止重复调用

C#:     return NotifyErrorAndReturnFalse("Looks like this method has been allready used");
MSIL:   IL_003b:  ldstr      "Looks like this method has been allready used"
        IL_0040:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
NEO:    06E3 45              [Looks like this method has been allready used]
        0711 NOP             []
        0712 CALL_I          [0101960C]

设置初始供应量

C#:     Storage.Put(Storage.CurrentContext, Owner, total_amount);
MSIL:   IL_0048:  call       Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_004d:  ldsfld     uint8[] Neo.SmartContract.ICO_Template::Owner
        IL_0052:  ldc.i8     0x16345785d8a0000
        IL_005b:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Implicit(uint64)
        IL_0060:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put
NEO:    0722 SYSCALL         [Neo.Storage.GetContext]
        073A NOP             []
        073B 20              [D9D45BA4DD9497C13A825196A459C6414DA4F020]
        0750 8               [00008A5D78456301]
        0759 NOP             []
        075A PUSH2           []//swap 0 and 2 param(0)
        075B XSWAP           []//(0)
        075C SYSCALL         [Neo.Storage.Put]

打印日志

C#:     Transferred(null, Owner, total_amount);
MSIL:   IL_009d:  callvirt   instance void Neo.SmartContract.ICO_Template/helo::Invoke(uint8[],
                                                                                    uint8[],
                                                                                    valuetype [System.Numerics]System.Numerics.BigInteger)
NEO:    07E0 SYSCALL         [Neo.Runtime.Notify]

转账合约函数调转逻辑

入口跳转

c#

    if (operation == "transfer")
    {
        if (args.Length != 3 || args[0] == null || ((byte[])args[0]).Length == 0 || args[1] == null || ((byte[])args[1]).Length == 0) return NotifyErrorAndReturnFalse("argument count must be 3 and they must not be null");
        byte[] from = (byte[])args[0];
        byte[] to = (byte[])args[1];
        BigInteger value = (BigInteger)args[2];
        return Transfer(from, to, value, false);
    }

MSIL

    IL_022d:  ldarg.0
    IL_022e:  ldstr      "transfer"
    IL_0233:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                    string)
    IL_0238:  stloc.s    V_17
    IL_023a:  ldloc.s    V_17
    IL_023c:  brfalse.s  IL_02b6
    IL_023e:  nop
    IL_023f:  ldarg.1
    IL_0240:  ldlen
    IL_0241:  conv.i4
    IL_0242:  ldc.i4.3
    IL_0243:  bne.un.s   IL_0268
    IL_0245:  ldarg.1
    IL_0246:  ldc.i4.0
    IL_0247:  ldelem.ref
    IL_0248:  brfalse.s  IL_0268
    IL_024a:  ldarg.1
    IL_024b:  ldc.i4.0
    IL_024c:  ldelem.ref
    IL_024d:  castclass  uint8[]
    IL_0252:  ldlen
    IL_0253:  brfalse.s  IL_0268
    IL_0255:  ldarg.1
    IL_0256:  ldc.i4.1
    IL_0257:  ldelem.ref
    IL_0258:  brfalse.s  IL_0268
    IL_025a:  ldarg.1
    IL_025b:  ldc.i4.1
    IL_025c:  ldelem.ref
    IL_025d:  castclass  uint8[]
    IL_0262:  ldlen
    IL_0263:  ldc.i4.0
    IL_0264:  ceq
    IL_0266:  br.s       IL_0269
    IL_0268:  ldc.i4.1
    IL_0269:  stloc.s    V_21
    IL_026b:  ldloc.s    V_21
    IL_026d:  brfalse.s  IL_0284
    IL_026f:  ldstr      "argument count must be 3 and they must not be null"
    IL_0274:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_0279:  box        [mscorlib]System.Boolean
    IL_027e:  stloc.2
    IL_027f:  br         IL_0326
    IL_0284:  ldarg.1
    IL_0285:  ldc.i4.0
    IL_0286:  ldelem.ref
    IL_0287:  castclass  uint8[]
    IL_028c:  stloc.s    V_18
    IL_028e:  ldarg.1
    IL_028f:  ldc.i4.1
    IL_0290:  ldelem.ref
    IL_0291:  castclass  uint8[]
    IL_0296:  stloc.s    V_19
    IL_0298:  ldarg.1
    IL_0299:  ldc.i4.2
    IL_029a:  ldelem.ref
    IL_029b:  unbox.any  [System.Numerics]System.Numerics.BigInteger
    IL_02a0:  stloc.s    V_20
    IL_02a2:  ldloc.s    V_18
    IL_02a4:  ldloc.s    V_19
    IL_02a6:  ldloc.s    V_20
    IL_02a8:  ldc.i4.0
    IL_02a9:  call       bool Neo.SmartContract.ICO_Template::Transfer(uint8[],
                                                                        uint8[],
                                                                        valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                        bool)
    IL_02ae:  box        [mscorlib]System.Boolean
    IL_02b3:  stloc.2
    IL_02b4:  br.s       IL_0326

VM OPCODE

    03CB FROMALTSTACK    []
    03CC DUP             []
    03CD TOALTSTACK      []
    03CE PUSH4           []
    03CF PUSH2           []
    03D0 ROLL            []
    03D1 SETITEM         []
    03D2 JMP             [FF01]
    03D5 FROMALTSTACK    []
    03D6 DUP             []
    03D7 TOALTSTACK      []
    03D8 PUSH0           []
    03D9 PICKITEM        []
    03DA 8               [transfer]
    03E3 EQUAL           []   //op_Equality
    03E4 FROMALTSTACK    []
    03E5 DUP             []
    03E6 TOALTSTACK      []
    03E7 PUSHBYTES1      []
    03E9 PUSH2           []
    03EA ROLL            []
    03EB SETITEM         []
    03EC FROMALTSTACK    []
    03ED DUP             []
    03EE TOALTSTACK      []
    03EF PUSHBYTES1      []
    03F1 PICKITEM        []
    03F2 JMPIFNOT        [00F8]
    03F5 NOP             []
    03F6 FROMALTSTACK    []
    03F7 DUP             []
    03F8 TOALTSTACK      []
    03F9 PUSHT           []
    03FA PICKITEM        []
    03FB ARRAYSIZE       []
    03FC PUSH3           []
    03FD ABS             []
    03FE SWAP            []
    03FF ABS             []
    0400 SWAP            []
    0401 NUMNOTEQUAL     []
    0402 JMPIF           [2F00]
    0405 FROMALTSTACK    []
    0406 DUP             []
    0407 TOALTSTACK      []
    0408 PUSHT           []
    0409 PICKITEM        []
    040A PUSH0           []
    040B PICKITEM        []
    040C JMPIFNOT        [0025]
    040F FROMALTSTACK    []
    0410 DUP             []
    0411 TOALTSTACK      []
    0412 PUSHT           []
    0413 PICKITEM        []
    0414 PUSH0           []
    0415 PICKITEM        []
    0416 ARRAYSIZE       []
    0417 JMPIFNOT        [001A]
    041A FROMALTSTACK    []
    041B DUP             []
    041C TOALTSTACK      []
    041D PUSHT           []
    041E PICKITEM        []
    041F PUSHT           []
    0420 PICKITEM        []
    0421 JMPIFNOT        [0010]
    0424 FROMALTSTACK    []
    0425 DUP             []
    0426 TOALTSTACK      []
    0427 PUSHT           []
    0428 PICKITEM        []
    0429 PUSHT           []
    042A PICKITEM        []
    042B ARRAYSIZE       []
    042C PUSH0           []
    042D NUMEQUAL        []
    042E JMP             [0400]
    0431 PUSHT           []
    0432 FROMALTSTACK    []
    0433 DUP             []
    0434 TOALTSTACK      []
    0435 PUSHBYTES1      []
    0437 PUSH2           []
    0438 ROLL            []
    0439 SETITEM         []
    043A FROMALTSTACK    []
    043B DUP             []
    043C TOALTSTACK      []
    043D PUSHBYTES1      []
    043F PICKITEM        []
    0440 JMPIFNOT        [0046]
    0443 50              [argument count must be 3 and they must not be null]
    0476 NOP             []
    0477 CALL_I          [0101310F]  //NotifyErrorAndReturnFalse
    047C FROMALTSTACK    []
    047D DUP             []
    047E TOALTSTACK      []
    047F PUSH4           []
    0480 PUSH2           []
    0481 ROLL            []
    0482 SETITEM         []
    0483 JMP             [4E01]
    0486 FROMALTSTACK    []
    0487 DUP             []
    0488 TOALTSTACK      []
    0489 PUSHT           []
    048A PICKITEM        []
    048B PUSH0           []
    048C PICKITEM        []
    048D FROMALTSTACK    []
    048E DUP             []
    048F TOALTSTACK      []
    0490 PUSHBYTES1      []
    0492 PUSH2           []
    0493 ROLL            []
    0494 SETITEM         []
    0495 FROMALTSTACK    []
    0496 DUP             []
    0497 TOALTSTACK      []
    0498 PUSHT           []
    0499 PICKITEM        []
    049A PUSHT           []
    049B PICKITEM        []
    049C FROMALTSTACK    []
    049D DUP             []
    049E TOALTSTACK      []
    049F PUSHBYTES1      []
    04A1 PUSH2           []
    04A2 ROLL            []
    04A3 SETITEM         []
    04A4 FROMALTSTACK    []
    04A5 DUP             []
    04A6 TOALTSTACK      []
    04A7 PUSHT           []
    04A8 PICKITEM        []
    04A9 PUSH2           []
    04AA PICKITEM        []
    04AB FROMALTSTACK    []
    04AC DUP             []
    04AD TOALTSTACK      []
    04AE PUSHBYTES1      []
    04B0 PUSH2           []
    04B1 ROLL            []
    04B2 SETITEM         []
    04B3 FROMALTSTACK    []
    04B4 DUP             []
    04B5 TOALTSTACK      []
    04B6 PUSHBYTES1      []
    04B8 PICKITEM        []
    04B9 FROMALTSTACK    []
    04BA DUP             []
    04BB TOALTSTACK      []
    04BC PUSHBYTES1      []
    04BE PICKITEM        []
    04BF FROMALTSTACK    []
    04C0 DUP             []
    04C1 TOALTSTACK      []
    04C2 PUSHBYTES1      []
    04C4 PICKITEM        []
    04C5 PUSH0           []
    04C6 NOP             []
    04C7 PUSH3           []//load3(0)
    04C8 PICK            []//(0)
    04C9 PUSHT           []//load01(0)
    04CA PICK            []//(0)
    04CB PUSH5           []//save to32(0)
    04CC XSWAP           []//(0)
    04CD DROP            []//(0)
    04CE PUSHT           []//save to01(0)
    04CF XSWAP           []//(0)
    04D0 DROP            []//(0)
    04D1 PUSH2           []//load2(0)
    04D2 PICK            []//(0)
    04D3 PUSH2           []//load11(0)
    04D4 PICK            []//(0)
    04D5 PUSH4           []//save to22(0)
    04D6 XSWAP           []//(0)
    04D7 DROP            []//(0)
    04D8 PUSH2           []//save to11(0)
    04D9 XSWAP           []//(0)
    04DA DROP            []//(0)
    04DB CALL_I          [0104E60A] //Transfer
    04E0 FROMALTSTACK    []
    04E1 DUP             []
    04E2 TOALTSTACK      []
    04E3 PUSH4           []
    04E4 PUSH2           []
    04E5 ROLL            []
    04E6 SETITEM         []

指令IL_0233/03E3用于确认要调用的函数是否是transfer,IL_0274/0477 用于打印错误日志信息,指令IL_02a9/04DB调用转账函数。

调用转账函数

c#

    if (to == null || to.Length != 20)
    return NotifyErrorAndReturnFalse("To value must not be empty and have size of 20");

    if (from == null || from.Length != 20)
        return NotifyErrorAndReturnFalse("From value must not be empty and have size of 20");

    if (value <= 0) return NotifyErrorAndReturnFalse("Try to send more than 0 tokens");
    if (!transferFrom && !Runtime.CheckWitness(from)) return NotifyErrorAndReturnFalse("Owner of the wallet is not involved in this invoke");
    if (from == to) return true;
    BigInteger from_value = Storage.Get(Storage.CurrentContext, from).AsBigInteger();
    if (from_value < value) return NotifyErrorAndReturnFalse("Insufficient funds");
    if (from_value == value)
        Storage.Delete(Storage.CurrentContext, from);
    else
        Storage.Put(Storage.CurrentContext, from, from_value - value);
    BigInteger to_value = Storage.Get(Storage.CurrentContext, to).AsBigInteger();
    Storage.Put(Storage.CurrentContext, to, to_value + value);
    Transferred(from, to, value);
    return true;

MSIL

    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  brfalse.s  IL_0010
    IL_0004:  ldarg.1
    IL_0005:  ldlen
    IL_0006:  conv.i4
    IL_0007:  ldc.i4.s   20
    IL_0009:  ceq
    IL_000b:  ldc.i4.0
    IL_000c:  ceq
    IL_000e:  br.s       IL_0011
    IL_0010:  ldc.i4.1
    IL_0011:  stloc.2
    IL_0012:  ldloc.2
    IL_0013:  brfalse.s  IL_0025
    IL_0015:  ldstr      "To value must not be empty and have size of 20"
    IL_001a:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_001f:  stloc.3
    IL_0020:  br         IL_012e
    IL_0025:  ldarg.0
    IL_0026:  brfalse.s  IL_0034
    IL_0028:  ldarg.0
    IL_0029:  ldlen
    IL_002a:  conv.i4
    IL_002b:  ldc.i4.s   20
    IL_002d:  ceq
    IL_002f:  ldc.i4.0
    IL_0030:  ceq
    IL_0032:  br.s       IL_0035
    IL_0034:  ldc.i4.1
    IL_0035:  stloc.s    V_4
    IL_0037:  ldloc.s    V_4
    IL_0039:  brfalse.s  IL_004b
    IL_003b:  ldstr      "From value must not be empty and have size of 20"
    IL_0040:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_0045:  stloc.3
    IL_0046:  br         IL_012e
    IL_004b:  ldarg.2
    IL_004c:  ldc.i4.0
    IL_004d:  conv.i8
    IL_004e:  call       bool [System.Numerics]System.Numerics.BigInteger::op_LessThanOrEqual(valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                                            int64)
    IL_0053:  stloc.s    V_5
    IL_0055:  ldloc.s    V_5
    IL_0057:  brfalse.s  IL_0069
    IL_0059:  ldstr      "Try to send more than 0 tokens"
    IL_005e:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_0063:  stloc.3
    IL_0064:  br         IL_012e
    IL_0069:  ldarg.3
    IL_006a:  brtrue.s   IL_0077
    IL_006c:  ldarg.0
    IL_006d:  call       bool [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Runtime::CheckWitness(uint8[])
    IL_0072:  ldc.i4.0
    IL_0073:  ceq
    IL_0075:  br.s       IL_0078
    IL_0077:  ldc.i4.0
    IL_0078:  stloc.s    V_6
    IL_007a:  ldloc.s    V_6
    IL_007c:  brfalse.s  IL_008e
    IL_007e:  ldstr      "Owner of the wallet is not involved in this invoke"
    IL_0083:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_0088:  stloc.3
    IL_0089:  br         IL_012e
    IL_008e:  ldarg.0
    IL_008f:  ldarg.1
    IL_0090:  ceq
    IL_0092:  stloc.s    V_7
    IL_0094:  ldloc.s    V_7
    IL_0096:  brfalse.s  IL_009f
    IL_0098:  ldc.i4.1
    IL_0099:  stloc.3
    IL_009a:  br         IL_012e
    IL_009f:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_00a4:  ldarg.0
    IL_00a5:  call       uint8[] [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Get(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                    uint8[])
    IL_00aa:  call       valuetype [System.Numerics]System.Numerics.BigInteger [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Helper::AsBigInteger(uint8[])
    IL_00af:  stloc.0
    IL_00b0:  ldloc.0
    IL_00b1:  ldarg.2
    IL_00b2:  call       bool [System.Numerics]System.Numerics.BigInteger::op_LessThan(valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                                        valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_00b7:  stloc.s    V_8
    IL_00b9:  ldloc.s    V_8
    IL_00bb:  brfalse.s  IL_00ca
    IL_00bd:  ldstr      "Insufficient funds"
    IL_00c2:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
    IL_00c7:  stloc.3
    IL_00c8:  br.s       IL_012e
    IL_00ca:  ldloc.0
    IL_00cb:  ldarg.2
    IL_00cc:  call       bool [System.Numerics]System.Numerics.BigInteger::op_Equality(valuetype [System.Numerics]System.Numerics.BigInteger,
                                                                                        valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_00d1:  stloc.s    V_9
    IL_00d3:  ldloc.s    V_9
    IL_00d5:  brfalse.s  IL_00e5
    IL_00d7:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_00dc:  ldarg.0
    IL_00dd:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Delete(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                    uint8[])
    IL_00e2:  nop
    IL_00e3:  br.s       IL_00f8
    IL_00e5:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_00ea:  ldarg.0
    IL_00eb:  ldloc.0
    IL_00ec:  ldarg.2
    IL_00ed:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Subtraction
    IL_00f2:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                uint8[],
                                                                                                                valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_00f7:  nop
    IL_00f8:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_00fd:  ldarg.1
    IL_00fe:  call       uint8[] [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Get(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                    uint8[])
    IL_0103:  call       valuetype [System.Numerics]System.Numerics.BigInteger [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Helper::AsBigInteger(uint8[])
    IL_0108:  stloc.1
    IL_0109:  call       class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
    IL_010e:  ldarg.1
    IL_010f:  ldloc.1
    IL_0110:  ldarg.2
    IL_0111:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Addition
    IL_0116:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put(class [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.StorageContext,
                                                                                                                uint8[],
                                                                                                                valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_011b:  nop
    IL_011c:  ldsfld     class Neo.SmartContract.ICO_Template/helo Neo.SmartContract.ICO_Template::Transferred
    IL_0121:  ldarg.0
    IL_0122:  ldarg.1
    IL_0123:  ldarg.2
    IL_0124:  callvirt   instance void Neo.SmartContract.ICO_Template/helo::Invoke(uint8[],
                                                                                    uint8[],
                                                                                    valuetype [System.Numerics]System.Numerics.BigInteger)
    IL_0129:  nop
    IL_012a:  ldc.i4.1
    IL_012b:  stloc.3
    IL_012c:  br.s       IL_012e
    IL_012e:  ldloc.3
    IL_012f:  ret

VM OPCODE

    0FC3 PUSH14          []//begincode(0)
    0FC4 NEWARRAY        []//(0)
    0FC5 TOALTSTACK      []//(0)
    0FC6 FROMALTSTACK    []//set param:0(0)
    0FC7 DUP             []
    0FC8 TOALTSTACK      []
    0FC9 PUSH0           []//(0)
    0FCA PUSH2           []//(0)
    0FCB ROLL            []
    0FCC SETITEM         []
    0FCD FROMALTSTACK    []//set param:1(0)
    0FCE DUP             []
    0FCF TOALTSTACK      []
    0FD0 PUSHT           []//(0)
    0FD1 PUSH2           []//(0)
    0FD2 ROLL            []
    0FD3 SETITEM         []
    0FD4 FROMALTSTACK    []//set param:2(0)
    0FD5 DUP             []
    0FD6 TOALTSTACK      []
    0FD7 PUSH2           []//(0)
    0FD8 PUSH2           []//(0)
    0FD9 ROLL            []
    0FDA SETITEM         []
    0FDB FROMALTSTACK    []//set param:3(0)
    0FDC DUP             []
    0FDD TOALTSTACK      []
    0FDE PUSH3           []//(0)
    0FDF PUSH2           []//(0)
    0FE0 ROLL            []
    0FE1 SETITEM         []
    0FE2 NOP             []
    0FE3 FROMALTSTACK    []
    0FE4 DUP             []
    0FE5 TOALTSTACK      []
    0FE6 PUSHT           []
    0FE7 PICKITEM        []
    0FE8 JMPIFNOT        [1100]
    0FEB FROMALTSTACK    []
    0FEC DUP             []
    0FED TOALTSTACK      []
    0FEE PUSHT           []
    0FEF PICKITEM        []
    0FF0 ARRAYSIZE       []
    0FF1 PUSHBYTES1      [14]
    0FF3 NUMEQUAL        []
    0FF4 PUSH0           []
    0FF5 NUMEQUAL        []
    0FF6 JMP             [0400]
    0FF9 PUSHT           []
    0FFA FROMALTSTACK    []
    0FFB DUP             []
    0FFC TOALTSTACK      []
    0FFD PUSH6           []
    0FFE PUSH2           []
    0FFF ROLL            []
    1000 SETITEM         []
    1001 FROMALTSTACK    []
    1002 DUP             []
    1003 TOALTSTACK      []
    1004 PUSH6           []
    1005 PICKITEM        []
    1006 JMPIFNOT        [4200]
    1009 46              [To value must not be empty and have size of 20]
    1038 NOP             []
    1039 CALL_I          [01016F03]
    103E FROMALTSTACK    []
    103F DUP             []
    1040 TOALTSTACK      []
    1041 PUSH7           []
    1042 PUSH2           []
    1043 ROLL            []
    1044 SETITEM         []
    1045 JMP             [0D03]
    1048 FROMALTSTACK    []
    1049 DUP             []
    104A TOALTSTACK      []
    104B PUSH0           []
    104C PICKITEM        []
    104D JMPIFNOT        [1100]
    1050 FROMALTSTACK    []
    1051 DUP             []
    1052 TOALTSTACK      []
    1053 PUSH0           []
    1054 PICKITEM        []
    1055 ARRAYSIZE       []
    1056 PUSHBYTES1      [14]
    1058 NUMEQUAL        []
    1059 PUSH0           []
    105A NUMEQUAL        []
    105B JMP             [0400]
    105E PUSHT           []
    105F FROMALTSTACK    []
    1060 DUP             []
    1061 TOALTSTACK      []
    1062 PUSH8           []
    1063 PUSH2           []
    1064 ROLL            []
    1065 SETITEM         []
    1066 FROMALTSTACK    []
    1067 DUP             []
    1068 TOALTSTACK      []
    1069 PUSH8           []
    106A PICKITEM        []
    106B JMPIFNOT        [4400]
    106E 48              [From value must not be empty and have size of 20]
    109F NOP             []
    10A0 CALL_I          [01010803]
    10A5 FROMALTSTACK    []
    10A6 DUP             []
    10A7 TOALTSTACK      []
    10A8 PUSH7           []
    10A9 PUSH2           []
    10AA ROLL            []
    10AB SETITEM         []
    10AC JMP             [A602]
    10AF FROMALTSTACK    []
    10B0 DUP             []
    10B1 TOALTSTACK      []
    10B2 PUSH2           []
    10B3 PICKITEM        []
    10B4 PUSH0           []
    10B5 LTE             []
    10B6 FROMALTSTACK    []
    10B7 DUP             []
    10B8 TOALTSTACK      []
    10B9 PUSH9           []
    10BA PUSH2           []
    10BB ROLL            []
    10BC SETITEM         []
    10BD FROMALTSTACK    []
    10BE DUP             []
    10BF TOALTSTACK      []
    10C0 PUSH9           []
    10C1 PICKITEM        []
    10C2 JMPIFNOT        [3200]
    10C5 30              [Try to send more than 0 tokens]
    10E4 NOP             []
    10E5 CALL_I          [0101C302]
    10EA FROMALTSTACK    []
    10EB DUP             []
    10EC TOALTSTACK      []
    10ED PUSH7           []
    10EE PUSH2           []
    10EF ROLL            []
    10F0 SETITEM         []
    10F1 JMP             [6102]
    10F4 FROMALTSTACK    []
    10F5 DUP             []
    10F6 TOALTSTACK      []
    10F7 PUSH3           []
    10F8 PICKITEM        []
    10F9 JMPIF           [2800]
    10FC FROMALTSTACK    []
    10FD DUP             []
    10FE TOALTSTACK      []
    10FF PUSH0           []
    1100 PICKITEM        []
    1101 NOP             []
    1102 SYSCALL         [Neo.Runtime.CheckWitness]
    111C PUSH0           []
    111D NUMEQUAL        []
    111E JMP             [0400]
    1121 PUSH0           []
    1122 FROMALTSTACK    []
    1123 DUP             []
    1124 TOALTSTACK      []
    1125 PUSH10          []
    1126 PUSH2           []
    1127 ROLL            []
    1128 SETITEM         []
    1129 FROMALTSTACK    []
    112A DUP             []
    112B TOALTSTACK      []
    112C PUSH10          []
    112D PICKITEM        []
    112E JMPIFNOT        [4600]
    1131 50              [Owner of the wallet is not involved in this invoke]
    1164 NOP             []
    1165 CALL_I          [01014302]
    116A FROMALTSTACK    []
    116B DUP             []
    116C TOALTSTACK      []
    116D PUSH7           []
    116E PUSH2           []
    116F ROLL            []
    1170 SETITEM         []
    1171 JMP             [E101]
    1174 FROMALTSTACK    []
    1175 DUP             []
    1176 TOALTSTACK      []
    1177 PUSH0           []
    1178 PICKITEM        []
    1179 FROMALTSTACK    []
    117A DUP             []
    117B TOALTSTACK      []
    117C PUSHT           []
    117D PICKITEM        []
    117E NUMEQUAL        []
    117F FROMALTSTACK    []
    1180 DUP             []
    1181 TOALTSTACK      []
    1182 PUSH11          []
    1183 PUSH2           []
    1184 ROLL            []
    1185 SETITEM         []
    1186 FROMALTSTACK    []
    1187 DUP             []
    1188 TOALTSTACK      []
    1189 PUSH11          []
    118A PICKITEM        []
    118B JMPIFNOT        [0E00]
    118E PUSHT           []
    118F FROMALTSTACK    []
    1190 DUP             []
    1191 TOALTSTACK      []
    1192 PUSH7           []
    1193 PUSH2           []
    1194 ROLL            []
    1195 SETITEM         []
    1196 JMP             [BC01]
    1199 NOP             []
    119A SYSCALL         [Neo.Storage.GetContext]
    11B2 FROMALTSTACK    []
    11B3 DUP             []
    11B4 TOALTSTACK      []
    11B5 PUSH0           []
    11B6 PICKITEM        []
    11B7 NOP             []
    11B8 SWAP            []//swap 2 param(0)
    11B9 SYSCALL         [Neo.Storage.Get]
    11CA FROMALTSTACK    []
    11CB DUP             []
    11CC TOALTSTACK      []
    11CD PUSH4           []
    11CE PUSH2           []
    11CF ROLL            []
    11D0 SETITEM         []
    11D1 FROMALTSTACK    []
    11D2 DUP             []
    11D3 TOALTSTACK      []
    11D4 PUSH4           []
    11D5 PICKITEM        []
    11D6 FROMALTSTACK    []
    11D7 DUP             []
    11D8 TOALTSTACK      []
    11D9 PUSH2           []
    11DA PICKITEM        []
    11DB LT              []
    11DC FROMALTSTACK    []
    11DD DUP             []
    11DE TOALTSTACK      []
    11DF PUSH12          []
    11E0 PUSH2           []
    11E1 ROLL            []
    11E2 SETITEM         []
    11E3 FROMALTSTACK    []
    11E4 DUP             []
    11E5 TOALTSTACK      []
    11E6 PUSH12          []
    11E7 PICKITEM        []
    11E8 JMPIFNOT        [2600]
    11EB 18              [496E73756666696369656E742066756E6473]
    11FE NOP             []
    11FF CALL_I          [0101A901]
    1204 FROMALTSTACK    []
    1205 DUP             []
    1206 TOALTSTACK      []
    1207 PUSH7           []
    1208 PUSH2           []
    1209 ROLL            []
    120A SETITEM         []
    120B JMP             [4701]
    120E FROMALTSTACK    []
    120F DUP             []
    1210 TOALTSTACK      []
    1211 PUSH4           []
    1212 PICKITEM        []
    1213 FROMALTSTACK    []
    1214 DUP             []
    1215 TOALTSTACK      []
    1216 PUSH2           []
    1217 PICKITEM        []
    1218 NUMEQUAL        []
    1219 FROMALTSTACK    []
    121A DUP             []
    121B TOALTSTACK      []
    121C PUSH13          []
    121D PUSH2           []
    121E ROLL            []
    121F SETITEM         []
    1220 FROMALTSTACK    []
    1221 DUP             []
    1222 TOALTSTACK      []
    1223 PUSH13          []
    1224 PICKITEM        []
    1225 JMPIFNOT        [3B00]
    1228 NOP             []
    1229 SYSCALL         [Neo.Storage.GetContext]
    1241 FROMALTSTACK    []
    1242 DUP             []
    1243 TOALTSTACK      []
    1244 PUSH0           []
    1245 PICKITEM        []
    1246 NOP             []
    1247 SWAP            []//swap 2 param(0)
    1248 SYSCALL         [Neo.Storage.Delete]
    125C NOP             []
    125D JMP             [4100]
    1260 NOP             []
    1261 SYSCALL         [Neo.Storage.GetContext]
    1279 FROMALTSTACK    []
    127A DUP             []
    127B TOALTSTACK      []
    127C PUSH0           []
    127D PICKITEM        []
    127E FROMALTSTACK    []
    127F DUP             []
    1280 TOALTSTACK      []
    1281 PUSH4           []
    1282 PICKITEM        []
    1283 FROMALTSTACK    []
    1284 DUP             []
    1285 TOALTSTACK      []
    1286 PUSH2           []
    1287 PICKITEM        []
    1288 SUB             []
    1289 NOP             []
    128A PUSH2           []//swap 0 and 2 param(0)
    128B XSWAP           []//(0)
    128C SYSCALL         [Neo.Storage.Put]
    129D NOP             []
    129E NOP             []
    129F SYSCALL         [Neo.Storage.GetContext]
    12B7 FROMALTSTACK    []
    12B8 DUP             []
    12B9 TOALTSTACK      []
    12BA PUSHT           []
    12BB PICKITEM        []
    12BC NOP             []
    12BD SWAP            []//swap 2 param(0)
    12BE SYSCALL         [Neo.Storage.Get]
    12CF FROMALTSTACK    []
    12D0 DUP             []
    12D1 TOALTSTACK      []
    12D2 PUSH5           []
    12D3 PUSH2           []
    12D4 ROLL            []
    12D5 SETITEM         []
    12D6 NOP             []
    12D7 SYSCALL         [Neo.Storage.GetContext]
    12EF FROMALTSTACK    []
    12F0 DUP             []
    12F1 TOALTSTACK      []
    12F2 PUSHT           []
    12F3 PICKITEM        []
    12F4 FROMALTSTACK    []
    12F5 DUP             []
    12F6 TOALTSTACK      []
    12F7 PUSH5           []
    12F8 PICKITEM        []
    12F9 FROMALTSTACK    []
    12FA DUP             []
    12FB TOALTSTACK      []
    12FC PUSH2           []
    12FD PICKITEM        []
    12FE ADD             []
    12FF NOP             []
    1300 PUSH2           []//swap 0 and 2 param(0)
    1301 XSWAP           []//(0)
    1302 SYSCALL         [Neo.Storage.Put]
    1313 NOP             []
    1314 NOP             []
    1315 FROMALTSTACK    []
    1316 DUP             []
    1317 TOALTSTACK      []
    1318 PUSH0           []
    1319 PICKITEM        []
    131A FROMALTSTACK    []
    131B DUP             []
    131C TOALTSTACK      []
    131D PUSHT           []
    131E PICKITEM        []
    131F FROMALTSTACK    []
    1320 DUP             []
    1321 TOALTSTACK      []
    1322 PUSH2           []
    1323 PICKITEM        []
    1324 NOP             []
    1325 PUSH2           []//swap 0 and 2 param(0)
    1326 XSWAP           []//(0)
    1327 8               [7472616E73666572]
    1330 PUSH4           []
    1331 PACK            []
    1332 SYSCALL         [Neo.Runtime.Notify]
    1346 NOP             []
    1347 PUSHT           []
    1348 FROMALTSTACK    []
    1349 DUP             []
    134A TOALTSTACK      []
    134B PUSH7           []
    134C PUSH2           []
    134D ROLL            []
    134E SETITEM         []
    134F JMP             [0300]
    1352 FROMALTSTACK    []
    1353 DUP             []
    1354 TOALTSTACK      []
    1355 PUSH7           []
    1356 PICKITEM        []
    1357 NOP             []
    1358 FROMALTSTACK    []//endcode(0)
    1359 DROP            []//(0)
    135A RET             []

提示转出地址不合法错误

C#:     return NotifyErrorAndReturnFalse("To value must not be empty and have size of 20");
MSIL:   IL_003b:  ldstr      "From value must not be empty and have size of 20"
        IL_0040:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
NEO:    1009 46              [To value must not be empty and have size of 20]
        1038 NOP             []
        1039 CALL_I          [01016F03]

提示转入地址不合法错误

C#:     return NotifyErrorAndReturnFalse("From value must not be empty and have size of 20");
MSIL:   IL_0015:  ldstr      "To value must not be empty and have size of 20"
        IL_001a:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
NEO:    106E 48              [From value must not be empty and have size of 20]
        109F NOP             []
        10A0 CALL_I          [01010803]

提示资产错误

C#:     return NotifyErrorAndReturnFalse("Try to send more than 0 tokens");  
MSIL:   IL_0059:  ldstr      "Try to send more than 0 tokens"
        IL_005e:  call       bool Neo.SmartContract.ICO_Template::NotifyErrorAndReturnFalse(string)
NEO:    10C5 30              [Try to send more than 0 tokens]
        10E4 NOP             []
        10E5 CALL_I          [0101C302]

获取转出地址资产

C#:     BigInteger from_value = Storage.Get(Storage.CurrentContext, from).AsBigInteger();   
MSIL:   IL_009f:  call   Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_00a4:  ldarg.0
        IL_00a5:  call       uint8[] [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Get
NEO:    119A SYSCALL         [Neo.Storage.GetContext]
        11B2 FROMALTSTACK    []
        11B3 DUP             []
        11B4 TOALTSTACK      []
        11B5 PUSH0           []
        11B6 PICKITEM        []
        11B7 NOP             []
        11B8 SWAP            []//swap 2 param(0)
        11B9 SYSCALL         [Neo.Storage.Get]

如果转出地址交易后金额为0删除该地址

C#:     Storage.Delete(Storage.CurrentContext, from);
MSIL:   IL_00d7:  call       Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_00dc:  ldarg.0
        IL_00dd:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Delete
NEO:    1229 SYSCALL         [Neo.Storage.GetContext]
        1241 FROMALTSTACK    []
        1242 DUP             []
        1243 TOALTSTACK      []
        1244 PUSH0           []
        1245 PICKITEM        []
        1246 NOP             []
        1247 SWAP            []//swap 2 param(0)
        1248 SYSCALL         [Neo.Storage.Delete]

如果转出地址交易后还有余额,则重新设置该地址余额

C#:     Storage.Put(Storage.CurrentContext, from, from_value - value);
MSIL:   IL_00e5:  call      Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_00ea:  ldarg.0
        IL_00eb:  ldloc.0
        IL_00ec:  ldarg.2
        IL_00ed:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Subtraction
        IL_00f2:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put
NEO:    1261 SYSCALL         [Neo.Storage.GetContext]
        1279 FROMALTSTACK    []
        127A DUP             []
        127B TOALTSTACK      []
        127C PUSH0           []
        127D PICKITEM        []
        127E FROMALTSTACK    []
        127F DUP             []
        1280 TOALTSTACK      []
        1281 PUSH4           []
        1282 PICKITEM        []
        1283 FROMALTSTACK    []
        1284 DUP             []
        1285 TOALTSTACK      []
        1286 PUSH2           []
        1287 PICKITEM        []
        1288 SUB             []
        1289 NOP             []
        128A PUSH2           []//swap 0 and 2 param(0)
        128B XSWAP           []//(0)
        128C SYSCALL         [Neo.Storage.Put]

设置转出地址交易后余额

C#:     Storage.Put(Storage.CurrentContext, to, to_value + value);
MSIL:   IL_0109:  call       Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::get_CurrentContext()
        IL_010e:  ldarg.1
        IL_010f:  ldloc.1
        IL_0110:  ldarg.2
        IL_0111:  call       valuetype [System.Numerics]System.Numerics.BigInteger [System.Numerics]System.Numerics.BigInteger::op_Addition
        IL_0116:  call       void [Neo.SmartContract.Framework]Neo.SmartContract.Framework.Services.Neo.Storage::Put
NEO:    12D7 SYSCALL         [Neo.Storage.GetContext]
        12EF FROMALTSTACK    []
        12F0 DUP             []
        12F1 TOALTSTACK      []
        12F2 PUSHT           []
        12F3 PICKITEM        []
        12F4 FROMALTSTACK    []
        12F5 DUP             []
        12F6 TOALTSTACK      []
        12F7 PUSH5           []
        12F8 PICKITEM        []
        12F9 FROMALTSTACK    []
        12FA DUP             []
        12FB TOALTSTACK      []
        12FC PUSH2           []
        12FD PICKITEM        []
        12FE ADD             []
        12FF NOP             []
        1300 PUSH2           []//swap 0 and 2 param(0)
        1301 XSWAP           []//(0)
        1302 SYSCALL         [Neo.Storage.Put]

打印输出日志

C#:     Transferred(from, to, value);
MSIL:   IL_0124:  callvirt   instance void Neo.SmartContract.ICO_Template/helo::Invoke(uint8[],
                                                                                    uint8[],
                                                                                    valuetype [System.Numerics]System.Numerics.BigInteger)
NEO:    1332 SYSCALL         [Neo.Runtime.Notify]

函数分类

当前使用的版本最常见的就是SYSCALL和CALL_I。

SYSCALL

用于调用外部接口函数,VM定义了一系列的外部接口,接口定义可以在Neo.SmartContract.Framework项目中查看,这些函数实现在Neo项目的SmartContract文件夹下面,函数使用SyscallAttribute标记,合约调用时候会按名索引到对应的执行函数。这里以Storage为例子说明这个问题。

定义

    文件位置:neo-devpack-dotnet\Neo.SmartContract.Framework\Services\Neo\Storage.cs
    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);

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

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, byte[] key, byte[] value);

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, byte[] key, BigInteger value);

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, byte[] key, string value);

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, string key, byte[] value);

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, string key, BigInteger value);

        [Syscall("Neo.Storage.Put")]
        public static extern void Put(StorageContext context, string key, string value);

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

        [Syscall("Neo.Storage.Delete")]
        public static extern void Delete(StorageContext context, string key);

        [Syscall("Neo.Storage.Find")]
        public static extern Iterator<byte[], byte[]> Find(StorageContext context, byte[] prefix);

        [Syscall("Neo.Storage.Find")]
        public static extern Iterator<string, byte[]> Find(StorageContext context, string prefix);
    }

实现

StateMachine继承自neo-vm\src\neo-vm\InteropService.cs文件中的InteropService,每次调用Register时,会在其中注册入实现函数,调用时通过名字查找到这个函数,在传入参数执行。

Storage_Put 具体实现

    文件位置:neo\SmartContract\StateMachine.cs 
    private bool Storage_Put(ExecutionEngine engine)
    {
        if (engine.EvaluationStack.Pop() is InteropInterface _interface)
        {
            StorageContext context = _interface.GetInterface<StorageContext>();
            if (context.IsReadOnly) return false;
            if (!CheckStorageContext(context)) return false;
            byte[] key = engine.EvaluationStack.Pop().GetByteArray();
            if (key.Length > 1024) return false;
            byte[] value = engine.EvaluationStack.Pop().GetByteArray();
            storages.GetAndChange(new StorageKey
            {
                ScriptHash = context.ScriptHash,
                Key = key
            }, () => new StorageItem()).Value = value;
            return true;
        }
        return false;
    }

注册函数实现

    Register("System.Storage.Put", Storage_Put);
    Register("System.Storage.Delete", Storage_Delete);

注册函数

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

转换

转换主要是通过判断函数是否有SyscallAttribute来确定,如果是的会把函数名称及参数信息添加上去。

判断函数

    文件位置:neo-compiler\neon\MSIL\Conv_Multi.cs
    public bool IsSysCall(Mono.Cecil.MethodDefinition defs, out string name)
    {
        if (defs == null)
        {
            name = "";
            return false;
        }
        foreach (var attr in defs.CustomAttributes)
        {
            if (attr.AttributeType.Name == "SyscallAttribute")
            {
                var type = attr.ConstructorArguments[0].Type;
                var value = (string)attr.ConstructorArguments[0].Value;

                //dosth
                name = value;
                return true;



            }
            //if(attr.t)
        }
        name = "";
        return false;
    }

转换Syscall

    var bytes = Encoding.UTF8.GetBytes(callname);
    if (bytes.Length > 252) throw new Exception("string is to long");
    byte[] outbytes = new byte[bytes.Length + 1];
    outbytes[0] = (byte)bytes.Length;
    Array.Copy(bytes, 0, outbytes, 1, bytes.Length);
    //bytes.Prepend 函数在 dotnet framework 4.6 编译不过
    _Convert1by1(VM.OpCode.SYSCALL, null, to, outbytes);
    return 0;

使用

    文件位置:neo-vm\src\neo-vm\ExecutionEngine.cs
    //VM虚拟机执行
    case OpCode.SYSCALL:
    if (!Service.Invoke(Encoding.ASCII.GetString(context.OpReader.ReadVarBytes(252)), this))
        State |= VMState.FAULT;
    break;

    文件位置:neo-vm\src\neo-vm\InteropService.cs
    //
    internal bool Invoke(string method, ExecutionEngine engine)
    {
        if (!dictionary.TryGetValue(method, out Func<ExecutionEngine, bool> func)) return false;
        return func(engine);
    }

CALL_I

用于合约内部函数调用

定义

在合约中自己定义的方法都符合这个类型,如例子中的转账及部署等。

    public static bool NotifyErrorAndReturnFalse(string value)
    {
        Runtime.Notify(value);
        return false;
    }

转换

判断,如果在当前使用的合约机器类库中存在该实现,则确定为这种类型的调用

    else if (this.outModule.mapMethods.ContainsKey(src.tokenMethod))
    {//this is a call
        calltype = 1;
    }

转换

    if (this.outModule.option.useNep8)
    {
        byte _pcount = (byte)defs.Parameters.Count;
        byte _rvcount = (byte)(defs.ReturnType.FullName == "System.Void" ? 0 : 1);

        var c = _Convert1by1(VM.OpCode.CALL_I, null, to, new byte[] { _rvcount, _pcount, 0, 0 });
        c.needfixfunc = true;
        c.srcfunc = src.tokenMethod;

    }
    else
    {
        var c = _Convert1by1(VM.OpCode.CALL, null, to, new byte[] { 5, 0 });
        c.needfixfunc = true;
        c.srcfunc = src.tokenMethod;
    }
    return 0;

使用

这种调用直接跳转到对应的指令位置执行。

    文件位置:neo-vm\src\neo-vm\ExecutionEngine.cs
    case OpCode.CALL:
        InvocationStack.Push(context.Clone());
        context.InstructionPointer += 2;
        ExecuteOp(OpCode.JMP, CurrentContext);
        break;

日志信息

日志提供了一种方式用于我们观察判断合约执行的状态,日志有两种使用方式,一种通过event定义日志,另一种直接调用Neo.Runtime.Notify函数,实质上呢,前者经过一系列的转换步骤后也是转换到Neo.Runtime.Notify逻辑当中,Neo.Runtime.Notify是一种SysCall,最终在执行器中注册如具体的Notify通知实现,因而这里自讨论下event的方式。

定义

    public delegate void transferDelegete(byte[] s1, byte[] s2, BigInteger  num);

    [DisplayName("transfer")]
    public static event transferDelegete Transferred;

转换

因为C#的事件直接执行,实际到MSIL层转换成Invoke调用,其中更多细节可以去研究下C#Delegate,event的具体实现机制,此处不多赘言。

事件解析:解析出来的东西并没啥用,只要在到处json时能看到个事件项目,函数实际上还是去搜索

    文件位置:neo-compiler\neon\MSIL\Converter.cs
    foreach (var e in t.Value.fields)
    {
        if (e.Value.isEvent)
        {
            NeoEvent ae = new NeoEvent();
            ae._namespace = e.Value.field.DeclaringType.FullName;
            ae.name = ae._namespace + "::" + e.Key;
            ae.displayName = e.Value.displayName;
            ae.returntype = e.Value.returntype;
            ae.paramtypes = e.Value.paramtypes;
            outModule.mapEvents[ae.name] = ae;
        }
    }

Invoke查找,之前有提过event和Nottify本质上是相同的,相同之处就体现在IsNotifyCall函数中,该函数会判断函数名称是否是Invoke,类型就粗暴的判断了Action。满足就是个NotifyCall了,返回的函数名就不用在意了,最后转换的时候给统统变成一个名字:Neo.Runtime.Notify。

    文件位置:neo-compiler\neon\MSIL\Conv_Multi.cs
    public bool IsNotifyCall(Mono.Cecil.MethodDefinition defs, Mono.Cecil.MethodReference refs, NeoMethod to, out string name)
    {

        name = to.lastsfieldname;
        if (to.lastsfieldname == null)
            return false;

        Mono.Cecil.TypeDefinition call = null;
        if (defs == null)
        {
            try
            {
                call = refs.DeclaringType.Resolve();
            }
            catch
            {//当不能取得这个,大半都是模板类

            }
        }
        else
        {
            call = defs.DeclaringType;
        }

        if (call != null)
        {
            if (call.BaseType.Name == "MulticastDelegate" || call.BaseType.Name == "Delegate")
            {
                to.lastsfieldname = null;
                return true;
            }
        }
        else//不能还原类型,只好用名字判断了
        {
            if (refs.Name == "Invoke" && refs.DeclaringType.Name.Contains("Action`"))
            {
                to.lastsfieldname = null;
                return true;
            }
        }
        name = "Notify";
        return false;
    }

转换

    //把name参数推进去
    var callp = Encoding.UTF8.GetBytes(callname);
    _ConvertPush(callp, src, to);

    //参数打包成array
    _ConvertPush(pcount + 1, null, to);
    _Convert1by1(VM.OpCode.PACK, null, to);

    //a syscall
    {
        var bytes = Encoding.UTF8.GetBytes("Neo.Runtime.Notify");
        byte[] outbytes = new byte[bytes.Length + 1];
        outbytes[0] = (byte)bytes.Length;
        Array.Copy(bytes, 0, outbytes, 1, bytes.Length);
        //bytes.Prepend 函数在 dotnet framework 4.6 编译不过
        _Convert1by1(VM.OpCode.SYSCALL, null, to, outbytes);
    }

Neo.Runtime.Notify 定义

    文件位置:neo-devpack-dotnet\Neo.SmartContract.Framework\Services\Neo\Runtime.cs
    public static class Runtime
    {
        public static extern TriggerType Trigger
        {
            [Syscall("Neo.Runtime.GetTrigger")]
            get;
        }

        public static extern uint Time
        {
            [Syscall("Neo.Runtime.GetTime")]
            get;
        }

        [Syscall("Neo.Runtime.CheckWitness")]
        public static extern bool CheckWitness(byte[] hashOrPubkey);

        [Syscall("Neo.Runtime.Notify")]
        public static extern void Notify(params object[] state);

        [Syscall("Neo.Runtime.Log")]
        public static extern void Log(string message);
    }

使用

c#中调用

    Transferred(null, Owner, total_amount);

Vm执行Neo.Runtime.Notify Syscall.

    文件位置:neo\SmartContract\StateReader.cs
    protected virtual bool Runtime_Notify(ExecutionEngine engine)
    {
        StackItem state = engine.EvaluationStack.Pop();
        NotifyEventArgs notification = new NotifyEventArgs(engine.ScriptContainer, new UInt160(engine.CurrentContext.ScriptHash), state);
        Notify?.Invoke(this, notification);
        notifications.Add(notification);
        return true;
    }

数据存储

Neo的数据存储主要通过Storage接口提供,外部实现的数据库存储,具体参考上面给的代码例子,Neo合约也不支持普通成员变量,但是静态成员是支持的,原因在于,静态成员编译即确定,在转换的过程中会在每个使用到静态变量的地方嵌入实际的值。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区