NEO合约编译过程牵涉到几个项目
c#的版本很多,从framework2.0到core2.3版本,语法差异很大,但是底层对应MSIL字节码没有变化,Neo的原理是先使用对应的编译器生成MSIL字节码,再把MSIL字节码转换成NEO vm的code码序列。这样做的好处在与利用了C#现有的语法成果,不必自己在设计一门语言,减少了合约编写的门槛。
大道不过三两行,说穿不值一文钱。下面我会通过一个完整的例子来说明这个流程,希望能藉此帮助更多人了解其中的原理细节,写出效率更好,费用更低的合约。
代码是在github上面找的,NEO-NEP5.1是NEP5的一个token,包含常用的元素,字段,事件与函数。具有常见的数据存储,合约调用及日志信息功能。
源码大致结构:实现细节摘除了,需要的请点击上面的链接
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转换合约字节码工具是在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。
用于调用外部接口函数,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);
}
用于合约内部函数调用
在合约中自己定义的方法都符合这个类型,如例子中的转账及部署等。
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合约也不支持普通成员变量,但是静态成员是支持的,原因在于,静态成员编译即确定,在转换的过程中会在每个使用到静态变量的地方嵌入实际的值。