Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
209 0 0
合约编写须知/ W- ]  V) ]3 o' J+ ~0 J
NEO 编译器支持的 C# 特性
. W3 R1 g- J  m" t8 ?0 [$ S# X使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
% u3 `9 E3 K+ S, z/ E# J# F# o2 z因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。, c' h0 B. l& Z7 b' o3 Q/ h% |
关于类型- {1 A% N& ]0 B& u$ s
NeoVM 有几种基本类型:2 j! C$ V: _( V8 B3 o5 t0 Q# j- ?. u& o
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    / q& Q9 h3 J! w# v

    # p& K' |4 C& A& U+ I而从 AVM 代码中能直接产生的基本类型只有:% T* Y1 L9 r) L* S" V. E' ^
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map; H9 N/ q; W# ]; r
    ( N* m; V1 @) N/ V0 F; M, U7 a+ U
    C# 的基本类型有
    # i( K0 V& g, E( v
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String; U% b! }2 ^- U6 J" ~  K3 m% u6 o2 ]# |

    ) k. ?3 s6 `3 @7 j0 y因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。" R3 P% w3 ], W3 J1 T; e+ }( `
    C# 整数类型的支持4 f- L6 I- \8 t2 n8 ^8 Z( Q
    Int8 int16 int32 int64 uint8 uint16 uint32 uint64$ z8 W3 T  L: k2 _" w
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。4 }' z* ~3 `' J- M! I- f: u* |
    一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
    1 V3 u' X4 y! [9 y  H* ^另外对 C# 的 BigInteger 也可以支持:7 l1 A# D$ M: M* U
    ulong total_neo = 200;3 C* ]( u. m6 @5 {, @
    BigInteger ico_neo = 300;3 i2 ?5 r: F7 b8 c& \% W8 `- ~8 Q& I
    BigInteger balance_neo = total_neo - ico_neo;# c$ a0 v4 t4 }3 ^' i- P8 Y
    ulong value = 150;0 q& u+ d' z' [& P
    需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    8 G' `( v; W; y& v对所有整数类型支持数学运算符 + - * / % 加减乘除余:+ a5 E; Q! T8 f6 d* m4 @- I
    var a1 = abc + 1;
    : z; n+ `  j$ C6 Dvar a2 = abc - 1;
    : S2 o$ Y  |" v! z; Uvar a3 = abc * 1;. L/ Z8 L6 A0 @: v+ Q* h
    var a4 = abc / 1;
    % x6 D) m$ D+ P$ U$ wvar a5 = abc % 2;
    # N: |3 g8 ]" `1 A. |对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    2 {$ H% \& B: w  G3 ?6 Cif (a1 > a2) ;
    ' e9 V) Z2 t# O+ |if (a2 = a2) ;
      A6 S. w3 o& h7 X! pif (a1
    0 k% o0 m( O) |  s5 H" _5 g  i支持整数的自增操作符:4 {: \! P. O( T5 W  c. }
    int k = 100;
      i( \/ ~8 [% B, mfor (int j = 0; j
    1 K! Q8 s  T6 b; q) M; V5 LC# 浮点类型的支持( E$ f7 J( H6 q5 F
    不支持0 C6 I& ^0 k* S
    C# bool 类型的支持% y, q5 U8 [% j. C1 L: o
    基本支持,底层行为和 INT 类似,false 为 int 0。* F$ Z, O: Z" c3 q3 v7 b3 |+ k9 S
    C# char string 类型的支持" l4 S: x7 L* D( ~- v$ m* a: Z
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。
    / ^  ?- m3 p' o) j尤其不要使用 string 处理中文。
    " {, P" l: w% U% X" u' T9 [( Xstring ss3 = "ab";
    & g" K. Q( d2 c( F# v3 {% dss3 += "c";
    $ x3 d; o! w5 R8 c/ E' G: kvar ss = "abcdef";
    + Z6 X* B! K0 t. @var b2 = ss.Length;
    $ c0 }) y) q7 `9 Y) W* L3 Q/ zvar c = ss + "abc";/ W) U0 e1 o  E4 Y% k
    var d = ss.Substring(1, 2);
    - x0 o1 R6 V7 d% Q支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。' j$ S/ _% O  M5 L9 }2 L# }# N
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。7 i. u( e& }- _8 D0 i2 s
    char 类型作为整数支持。& ~' Y2 c; W0 z7 O! S
    C# class 和 结构体的支持
    3 }- z  O. o) l; }) R4 q6 p$ z支持 C# class 和结构体定义。
    + h5 ]& q: K( `' n% r- jpublic class info! m  U  e; n" n
    {9 u# u: X0 [, M+ J) D1 i6 k
        public byte[] a;& G- }0 `+ Y: l. K/ `
        public byte[] b;1 S0 a, i" d2 S$ K: b/ P  v2 d
    }
    * [, Y) ?4 ]5 z不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    9 N, C  B/ h$ N9 d不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。
    / r  c4 @5 }# Q" p( O7 ^C# 数组的支持
    4 ^; Y& d" ?% P  b! @- \+ r: y数组支持,行为基本和 C# 一致。
    9 s7 a; ?$ \/ K: ^" PByte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    + d# v' B9 j0 V8 g6 m对一般数组可以用的设置其中的值的操作
    8 `5 I2 I$ m$ I( {: l2 J  ^short[] some = new short[17];$ O  d* i0 s4 J0 x
    some[1] = 12;
    + r1 t, ?+ u' c: Zreturn some;% {; @' s3 q% a8 z4 `* ^$ c% _
    对 Byte[] 不允许。5 b) @$ d1 |# T
    C# 枚举的支持
    4 N- u+ i  Z2 S' ~9 H支持仅作为数值使用时定义枚举。
    4 j+ O8 h) R$ O+ L' X$ u  p不支持格式化为 String,以及从 String 解析等。
    , O* |, h+ J7 G9 \. wC# 容器的支持
    2 e0 f- N- J( F6 k9 W9 O不支持 C# 常用的 LIST Dictionary 容器。9 g, n6 T# l* I7 Q3 H: O3 U; k
    LIST 功能可以用数组替代。, p# @4 Q. z( }7 ^
    Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    2 w/ j( \0 z% \: i" h  |C# 变量的支持
    / E+ Z+ N8 j$ U临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。- J- N/ G3 D% E8 u. u, G& s
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;* O  O; g7 K& i
    public static BigInteger TotalIcoNeo() => total_neo;, r; K, e- ?3 x/ F5 }
    C# 委托和事件的支持
    + {. `: M6 d. Y7 T8 SC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。, ?0 W% L5 ]8 B- ]4 S/ g* y
    public delegate void acall(string a);
    4 g* G: r+ I1 [  e9 @一是可以用来定义事件:
    , a" ]; O) R& kpublic static event acall dododo;( e9 K1 Q' i5 `/ Y, j) h
    调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。" r! D% {# b! z+ ~& O
    另一个是可以将一个 bytearray 转型为一个委托:
    ; y& K" D" q: z9 S& l# T3 L' hacall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();$ L! q5 V5 w* H# X
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。
    % U) E9 u# E' h3 ^+ p$ xC# 开发约定1 Y2 M  ]( e) }  J' g1 C
    C# 的导出要求! o, T) Z' _9 h! I) T# f1 J" Y
    NEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
      J5 ?1 U# W# `其它要导出的函数都应该为 public static,且不可重名。9 V& l5 N0 X; x
    C# 的委托和定义
    8 k* p# y. }5 B" o& Q$ xC# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。
    / ?" V/ b3 U# W/ @) S/ l% G分别对应 NEO 智能合约的通知与 NEP4。" @. ?7 y, j) L/ _' Y  h$ u! [
    内置特性# F; V3 W- M# m  v
    如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    4 l9 b7 z; ]2 {) E  r4 x: D% k  D也可以在你的智能合约代码中使用这些功能。
    . P- T2 P: y8 }APPCALL特性
    7 d5 `9 e# e6 [; @5 q调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。
    + X7 m, g# b2 [. {0 Q8 I[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]) o7 j) o( {7 }' M+ D( K
    public static extern bool AnotherContract(string arg, object[] args);9 b$ q$ [# ^; r- O
    SYSCALL特性$ W& d7 e! `- I0 Q3 p' q8 C$ i
    调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:, z# L" m2 j3 b* J6 O* B7 R8 t% c4 ^
    [Syscall("Neo.Account.GetBalance")]% `2 E6 t8 W' l! a) u
    public extern long GetBalance(byte[] asset_id);
    , n4 N8 o& R; Y) c+ ?7 H! KOPCALL 特性* `! C) |. X- M+ Q& h
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:4 Y4 X: n- |* x! q0 L* _  w
    [OpCode(Neo.VM.OpCode.LEFT)]
    , R9 m9 f7 ]: h& Apublic extern static byte[] Take(byte[] good, int index);( F  X9 r& X- {& ?3 z. R
    NONEMIT 特性
    6 ?% E, w- V5 Q- F  f执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。0 ?2 m1 h0 O: g; a: r( N
    [Nonemit]& J+ L/ B* {5 T6 \5 y9 S: b- j
    public extern static Delegate ToDelegate(this byte[] source);4 _0 Z  f; ?6 t3 _- d
    NonemitWithConvert 特性
    9 u: B* L' Q7 Y5 k5 Q$ X当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。; s# h# f5 g, {( q) e7 f4 I
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]
    4 x1 Q. P& _1 k% [" P) u0 {public extern static byte[] ToScriptHash(this string address);1 Y3 ?6 I' F% J* l8 ?) |8 W
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。
    8 M+ l5 z6 t4 c: q/ [而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11