Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
86 0 0
合约编写须知
2 z. o0 r! u' U' ENEO 编译器支持的 C# 特性. h/ j/ [' t* A  H+ c
使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。# B* y' |7 F- w. h& |# d1 E6 d& G
因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。4 R3 y0 c9 Z4 e4 C' H
关于类型/ s9 j/ P6 ]7 w  v1 l4 e( P
NeoVM 有几种基本类型:6 y  P: I* K7 X
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    ; q) U* M; p0 {( h0 c( n

    4 x! C' G- K) ^; I而从 AVM 代码中能直接产生的基本类型只有:
    " |5 ?8 |8 d5 j/ ?( B* t( h
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map
    9 I4 s0 O& ?2 b  _* Q9 a/ W2 z. b
    5 N& E$ j: A( @4 b+ f2 ?% e0 L$ z  F2 D
    C# 的基本类型有
    / b% w4 h0 {& E
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String
    . J4 _2 j; r: T
    - o' F% E. F+ @7 D" ~/ P! P
    因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。
    / h$ |; r, ~( @: U, lC# 整数类型的支持
    2 `; H# d) k* rInt8 int16 int32 int64 uint8 uint16 uint32 uint64
    & P9 y7 \" ?, G) H' w6 B因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    & a! Y( F$ I. J- I/ I6 o一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
      I! Y( S5 e, X1 P另外对 C# 的 BigInteger 也可以支持:
    ; f: O: B& ?- q8 J; b) e3 Aulong total_neo = 200;
    & u! Y6 B0 }& v$ `BigInteger ico_neo = 300;
    % s- n' {0 ^( ?  cBigInteger balance_neo = total_neo - ico_neo;2 |' Y% f4 X: Z
    ulong value = 150;
    8 l' i1 n3 h9 Q需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    % ?, f4 ^& ^" u$ x对所有整数类型支持数学运算符 + - * / % 加减乘除余:
    6 o- n. j: H6 W% Avar a1 = abc + 1;/ d0 t- O8 _3 Y: ]% r- y
    var a2 = abc - 1;8 L( I2 G- z6 j" x$ o9 A  G7 d3 c
    var a3 = abc * 1;* k6 ?( D$ R) G( s4 N- Y& M2 X
    var a4 = abc / 1;) Z# T8 Y% d* \( }
    var a5 = abc % 2;
    * T7 N" \' }+ _对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:( V* M. E) N% D* @2 ]$ W
    if (a1 > a2) ;
    5 N. A2 O& P+ Z- D, X  I% D$ [if (a2 = a2) ;0 r9 K2 m; [8 [# \1 o
    if (a1
    ' H# J- r% Z' h1 F支持整数的自增操作符:- T) I) u; \* A+ G
    int k = 100;
    & q$ S, U, ~$ z9 j* l* O# zfor (int j = 0; j
    # l# |6 Z2 l+ n8 LC# 浮点类型的支持
    * U9 j4 k! F1 e: U) y! D: {不支持
    1 t) ^+ v5 F  Q6 h% _' g3 oC# bool 类型的支持
    4 X2 Z" H4 r+ M基本支持,底层行为和 INT 类似,false 为 int 0。: l4 ?  d, |" @( D2 ]
    C# char string 类型的支持
    1 C! j) K! l9 X不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。
    . d4 x: k2 r* Y8 h  K. s尤其不要使用 string 处理中文。# X4 v  `! j, O' N
    string ss3 = "ab";2 C! K( a+ n# G- }$ u
    ss3 += "c";
    + K4 J. T& P. Zvar ss = "abcdef";0 N- f2 i6 k3 t4 S+ E7 E" R( D2 `: y
    var b2 = ss.Length;
    : R  ?3 |% t/ e! q1 g1 }- T* D% I$ tvar c = ss + "abc";: u+ X* P' S: h- v; L
    var d = ss.Substring(1, 2);
    9 l* Y5 U* O0 S) L( E% a. o) K支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。9 y" E  P6 K/ H$ X0 _# B- b6 {
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。
    $ o( @0 }9 l; V( Cchar 类型作为整数支持。# }) }' Q* C; L8 T" q2 c
    C# class 和 结构体的支持
    4 q% |1 `" l! ?8 }( G, G/ Z1 p支持 C# class 和结构体定义。
    / N$ Q0 r- E* t7 [5 i. {% Gpublic class info5 C- _' I8 G" z4 E4 [
    {. R2 z" s7 k" G. l/ ]# h
        public byte[] a;; `: \9 H' p' w
        public byte[] b;
    0 [2 Q! k1 L3 B0 r8 f, L, g}, Q+ m( C+ ]* l; Y; c
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    8 e- K" }6 _# ^+ A! s不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。- r) i, u8 J2 C  H
    C# 数组的支持& d2 ~: s6 O1 I* r
    数组支持,行为基本和 C# 一致。6 r5 X) |+ @5 n- j# V% J& [0 f8 ?
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。* }4 q% p+ u; d4 l$ f
    对一般数组可以用的设置其中的值的操作
    : p( e+ j( A3 sshort[] some = new short[17];
    $ b  l  T* W) I3 m7 X3 T. }4 isome[1] = 12;
    , l% a: A3 a2 z" k0 h# k& Xreturn some;
    0 ]2 S7 O+ M3 _! \) ?& `对 Byte[] 不允许。
    0 U- i% N# E6 Z; z$ ^  k, `5 n5 oC# 枚举的支持% F1 s3 C! c& b
    支持仅作为数值使用时定义枚举。
    - _  m7 |: t3 t" u) q不支持格式化为 String,以及从 String 解析等。8 D' g; ]; g" h
    C# 容器的支持7 x/ O, w( D5 I/ o
    不支持 C# 常用的 LIST Dictionary 容器。
      j" }; V1 r; B5 W( gLIST 功能可以用数组替代。! o. @, F1 f% K& k/ F* W, F
    Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    . x5 h7 _; A. \* Q( jC# 变量的支持7 a: O( L6 H* H
    临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。# g) }0 {8 _9 E+ n8 E4 V6 S$ b
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;" C" x0 l/ L' V9 P. [& L! Y
    public static BigInteger TotalIcoNeo() => total_neo;
    8 `: B1 i2 ~! U: MC# 委托和事件的支持
    % G; U$ V8 J/ H# ?C# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
      [) \: c4 ^0 l7 Q9 z2 v( |8 v% ]public delegate void acall(string a);1 r& q& p) A, I9 z1 m2 V+ }
    一是可以用来定义事件:: v' `, `# w" R% ]1 j. e
    public static event acall dododo;
    5 ]% n# A6 `% @调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。$ u1 L  R) ^  r) u1 T
    另一个是可以将一个 bytearray 转型为一个委托:
    . y- k' a7 R. k0 wacall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();
    ! \# Y! T, s- I- C$ Z& q这就实现了对一个指定地址的智能合约的调用,参考 NEP4。& e. ]4 l8 _$ _
    C# 开发约定
    / E, z$ Z4 ?! Y5 x" _C# 的导出要求' t$ O# i+ |, `% Z
    NEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    4 K" ]! f+ m; w. z% p! C7 M/ k3 k其它要导出的函数都应该为 public static,且不可重名。
    * ^" Q0 X  q8 l! [C# 的委托和定义: @- B' \: G' R* f+ Z: p% ~# X
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。) B2 B0 ]2 p  \$ w3 L. `, O, Y
    分别对应 NEO 智能合约的通知与 NEP4。9 c! ^$ y# x9 p4 D
    内置特性7 l2 _" j1 Y- Z  x* e- W
    如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    5 p# j2 [% o# [1 w$ N. b- Q5 _也可以在你的智能合约代码中使用这些功能。! \0 q) i' T8 x$ b
    APPCALL特性$ T1 X) G3 J4 ?$ S% e/ |
    调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。# s  h2 J- l0 ?: d8 x
    [Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
    9 g0 w4 n4 @- K, o# U% _4 y% {$ Qpublic static extern bool AnotherContract(string arg, object[] args);# N* L0 ?3 v( r. @: Q/ Q
    SYSCALL特性  e' s" n# q! B, @2 r- s7 V& M
    调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    2 i- O" m. I7 T[Syscall("Neo.Account.GetBalance")]
    6 E& ^7 q# V- f$ Qpublic extern long GetBalance(byte[] asset_id);. o+ z5 g4 P7 L. U
    OPCALL 特性/ r# P6 y/ j: d3 J# N* x
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:% P$ R$ l, Y( z+ o3 I; m, j' s
    [OpCode(Neo.VM.OpCode.LEFT)]
    0 e: W$ m1 M( l7 ^$ V0 s. }% mpublic extern static byte[] Take(byte[] good, int index);
    - l9 ?, ]/ z- Y0 @NONEMIT 特性  g, N( ^& e# g( q7 _5 B& d
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。: Z+ S$ k1 ?+ z3 j
    [Nonemit]/ ~; }7 e+ }( k4 A/ F& _
    public extern static Delegate ToDelegate(this byte[] source);
      m6 ?0 Z) @% _* ~5 \/ ~; z  `# c# XNonemitWithConvert 特性
    2 Z8 l6 f. D2 Q' M$ O0 I当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。
    # a0 P6 X4 {* j. H4 Y$ d[NonemitWithConvert(ConvertMethod.ToScriptHash)]: c  Y) C$ u; z8 Y; a+ d' p/ T
    public extern static byte[] ToScriptHash(this string address);) O6 D8 I$ q* B# q# j, }" j
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。
    . L3 s5 S1 a! g而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11