Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
85 0 0
合约编写须知9 a' \6 D8 J* m2 z
NEO 编译器支持的 C# 特性, r4 l- u6 `' }  W
使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
* U, e2 p; B6 U* w因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
& m" A% x) X1 K' M+ O4 u关于类型
2 V- u& P  k4 v5 p1 |) v3 _NeoVM 有几种基本类型:, b- \% Z# h0 _+ b# b! ], L5 ]0 ~
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    . Y, p5 _! M% {+ K* P& [2 u) p1 W
    + Q7 a# u2 \) ?
    而从 AVM 代码中能直接产生的基本类型只有:
    ' U& i* _- T( Y
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map
    : n6 C7 _; |2 Z9 J. I7 Q

    8 y) C/ l- K8 Q+ q. s& D' A7 zC# 的基本类型有
    7 X( ]; a5 A) \9 a* K' m: I( [
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String+ I" S+ K9 a$ E" a

    8 b/ p% E! U/ k1 D1 q因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。
    , d2 y4 o. y+ y3 u+ x/ {( E! }C# 整数类型的支持! A& H$ j& g- E- f! X6 b" D/ j
    Int8 int16 int32 int64 uint8 uint16 uint32 uint64; a- Z. O; O4 y9 x# m: ^( b
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    6 z: m: m* a+ |( n一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。" }7 `( D2 f& J; h2 J! u! |/ h( }
    另外对 C# 的 BigInteger 也可以支持:# Z3 d5 F( J% @7 Q" Q# g/ f
    ulong total_neo = 200;
    # r' u6 Y) \% x" SBigInteger ico_neo = 300;9 f) j5 y& A' m% j6 U3 _
    BigInteger balance_neo = total_neo - ico_neo;, y% O: i+ e0 K) x
    ulong value = 150;, }; P" C  [; a" C) Z
    需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    6 y& c8 B# s  U! `$ F9 U对所有整数类型支持数学运算符 + - * / % 加减乘除余:
    ! h+ K- V% i- b# G0 r9 k/ \var a1 = abc + 1;
    2 I: Q" @* j2 Z' }& W9 F! wvar a2 = abc - 1;. {) \, `; o/ X- U  @6 g
    var a3 = abc * 1;; b9 q" [5 R, ^0 ~( Z8 n+ H
    var a4 = abc / 1;
    ( u: z( H( H7 X6 K' Bvar a5 = abc % 2;
    3 X' Y9 T5 k+ A! i, }2 J1 }8 V对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:9 v& C" H  z8 [
    if (a1 > a2) ;- {$ [4 ?" {5 t% M4 J# ^$ P
    if (a2 = a2) ;0 X  ]5 {0 o/ w/ Q6 r
    if (a1 4 c; k: M6 _% A9 x
    支持整数的自增操作符:. H/ j+ U$ F$ \( V
    int k = 100;8 v* v% ^, S$ E6 p. m
    for (int j = 0; j
    & D: R9 s& J7 C7 U& o$ \C# 浮点类型的支持' m7 @3 w# i/ k1 P' k5 K# m
    不支持5 f" |# b, h: d2 @2 ?7 L
    C# bool 类型的支持
    & O2 R+ \" q; C. h基本支持,底层行为和 INT 类似,false 为 int 0。' s- D- e3 s; A) \8 I/ A$ H
    C# char string 类型的支持  }% Y- |9 A" Z$ T7 g
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。: U0 J& p- F5 O4 t0 Z/ N1 o0 g
    尤其不要使用 string 处理中文。! V( l0 o7 C- A
    string ss3 = "ab";, n3 D! ~: G2 }
    ss3 += "c";2 P" u) L" E% G/ w2 l0 s4 [  [
    var ss = "abcdef";) A1 h2 L+ H3 V7 J; X# C) B/ o
    var b2 = ss.Length;8 O0 J$ W) V& G+ a4 {" _) f
    var c = ss + "abc";3 N7 x/ N) p2 L8 D1 g- b
    var d = ss.Substring(1, 2);
    ; X* x! w5 K4 W, b  D8 r- E) y支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。* K+ d% a8 ]0 U0 |% L
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。
    + z5 ?( \3 `. L1 |3 ichar 类型作为整数支持。
    6 |" O' e  _4 {! [' N2 ^9 j$ _C# class 和 结构体的支持7 m% _9 ?9 c) I8 l# T
    支持 C# class 和结构体定义。. H- C* _- R& W" S0 Q
    public class info, ?# w4 n! m( {% b
    {! }3 z- ]; f/ }' q
        public byte[] a;2 M* |; o* p6 p
        public byte[] b;' V& S- n2 k- o/ e
    }  q/ x6 {: S7 P; P2 z
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    % E1 e( j! M# ]) ?不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。) ^  L$ c% K- i) b6 B5 t+ _* `
    C# 数组的支持
    & g! `9 k! K8 _, q8 G数组支持,行为基本和 C# 一致。  ~. u, c; n( H
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    2 X9 s' W( b3 X1 _7 [对一般数组可以用的设置其中的值的操作
    1 i) ?( [$ q9 t8 g6 ^  Bshort[] some = new short[17];
    , K) P  C; t0 ~, Z  N. c: i/ Bsome[1] = 12;- M( A, y/ H* G9 l% A! w8 }
    return some;
    & \5 a/ K7 ]6 j  O6 N对 Byte[] 不允许。& U4 x$ z, d- Z3 C
    C# 枚举的支持
    $ G; j6 m' Z- i8 k+ ?3 c# g& s1 ?支持仅作为数值使用时定义枚举。& A6 M5 {2 A" B; K6 \- l) }) a
    不支持格式化为 String,以及从 String 解析等。
    2 T& B. A* f" Q2 R( ~7 kC# 容器的支持' E# T- w; Q, g9 m+ Z
    不支持 C# 常用的 LIST Dictionary 容器。
    1 _- g  h0 a# m/ f' F% y% K4 fLIST 功能可以用数组替代。
    1 V! P. a  G* T0 uDictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。8 z! [: u+ d5 c  X. `" c6 h+ N: e
    C# 变量的支持) o* Y) s: K( R" L+ t, y. ~8 j- S6 x
    临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。/ E5 s# g2 H/ w! t9 l; i. |( U+ ~( n
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
    + B. o5 g" I8 b# T2 ?# @8 F) `public static BigInteger TotalIcoNeo() => total_neo;
    ; e: j* ^0 r  J2 ^8 zC# 委托和事件的支持
    3 X2 G; e9 v" c9 w& Q3 M  D6 dC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
      A4 e6 C7 Q+ a6 [public delegate void acall(string a);
    : d# B3 ]+ A1 h/ _3 T一是可以用来定义事件:
    - W1 i5 _* h0 [# c; T9 Q; F& `$ cpublic static event acall dododo;
    - K' O, R$ \4 I  K( X0 \调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。
    " I% r' e. c$ j6 a& F另一个是可以将一个 bytearray 转型为一个委托:
    3 l5 ^2 t% p1 V4 I, y" q! y/ Macall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();/ p; I, ?) R! [5 I
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。) M" f3 Y& |6 ~+ @- V0 O
    C# 开发约定5 q. I& o9 V0 }( f
    C# 的导出要求
    " x$ B7 c% O& JNEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    1 T5 W; v+ _9 C7 _( F  b其它要导出的函数都应该为 public static,且不可重名。
    " j; j2 f! M$ K0 k. x# {) XC# 的委托和定义
    3 F( }+ T, v) J  k! r" M. |+ ^C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。- F& s. o. }% o( X( c: ]! t
    分别对应 NEO 智能合约的通知与 NEP4。
    $ J. K& X/ t' v; a% D# C; `内置特性2 d5 d- j" |1 y% ]/ x9 `& a
    如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。( T  N- @$ G. M
    也可以在你的智能合约代码中使用这些功能。. l3 Y. O" v( ]5 x
    APPCALL特性
    * d! d" i8 g! f2 E/ {2 F% C4 O3 X调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。4 f- N3 V6 p  U% g8 K" m% v
    [Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]- Y/ \0 d# H' M! ]- j+ Y3 v: {' `
    public static extern bool AnotherContract(string arg, object[] args);2 {7 q+ M4 C5 c2 k7 T& a
    SYSCALL特性( [2 z  [9 k# i' m; [
    调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    ( j( U* e5 U* m- N& u: ~[Syscall("Neo.Account.GetBalance")]' p, s7 E% \& `+ H
    public extern long GetBalance(byte[] asset_id);
    . l2 ^: f/ w* ~* MOPCALL 特性1 O' i  J, R" |1 z
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:
    . p6 Q% H3 b' J8 r[OpCode(Neo.VM.OpCode.LEFT)]2 P2 I7 [: m) ~: [/ C& d5 W
    public extern static byte[] Take(byte[] good, int index);3 y$ v3 J( T, m% w' p" o2 o" M2 o
    NONEMIT 特性4 o! ~  n! H+ h4 J$ O
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。
    $ _/ Z3 Z4 B2 ^& l[Nonemit]
    2 J9 g! ~! d+ u5 B7 b, Zpublic extern static Delegate ToDelegate(this byte[] source);  n; {) F" f) v4 F& i6 F' ~
    NonemitWithConvert 特性
    7 p1 {1 Z- o7 \* U* ^当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。
    ! d* ]+ `8 Z* m1 ~: `. F& O- f[NonemitWithConvert(ConvertMethod.ToScriptHash)]- x! U6 Y* X: G
    public extern static byte[] ToScriptHash(this string address);" C3 }+ j6 ^2 y  h
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。
    1 k. I' {% [5 e  Q而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11