Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
180 0 0
合约编写须知9 k: M, d# X% Y# J4 d2 F
NEO 编译器支持的 C# 特性
! q9 \" `6 t' R- o& C3 {5 P8 u使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
9 _: i9 X, T" d2 \8 W& W: m因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
9 b* N2 Q. ], _+ ?9 T( r关于类型/ P) ]8 Y+ b2 I- w1 Z4 U+ U
NeoVM 有几种基本类型:
% X* T/ S% c) x4 F9 j# z
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    1 v) ~4 w2 Q6 `
    5 c- k$ N* Z. W$ {& a# G
    而从 AVM 代码中能直接产生的基本类型只有:
    ) E& e& [7 ~+ T+ W9 n+ s6 y
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map
    & ^4 T" A$ ~* h! P
      D3 _, v. D" U
    C# 的基本类型有1 H7 Y. L7 ^1 q
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String/ L& O" q" H& b& D+ r: ~. ?

    - g2 G6 |9 A9 e' W7 g7 L因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。1 p4 _/ a0 d# B! }) x( o
    C# 整数类型的支持) ~, n( j* E7 O0 d5 g0 @9 }
    Int8 int16 int32 int64 uint8 uint16 uint32 uint649 c  O) ~* H6 E/ \% ?2 e& b
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。, _5 s. _) {3 T/ D2 y
    一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
    # N3 F  S$ F/ q, ]0 l另外对 C# 的 BigInteger 也可以支持:: h* ~( h( r; E* Y
    ulong total_neo = 200;" X9 ~/ ?, ~' H
    BigInteger ico_neo = 300;0 K$ r! I3 R& L6 b; B6 S
    BigInteger balance_neo = total_neo - ico_neo;
    % F5 s, Z- u/ g1 rulong value = 150;
    * D2 \! {" y/ c- t& P需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    ! P' C) H6 P2 W3 d  C对所有整数类型支持数学运算符 + - * / % 加减乘除余:
    * A2 R3 t- @$ g( C) `0 l- H- Jvar a1 = abc + 1;
    ' @4 x% K4 s1 Wvar a2 = abc - 1;8 V5 S' w4 M9 B0 N; y0 z0 b6 U
    var a3 = abc * 1;, i: p6 R1 k, |. v% p
    var a4 = abc / 1;% s% h% H# S; {' D0 B, f
    var a5 = abc % 2;$ O+ }) P; n% N* m& S
    对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    % ]* }' @+ G5 f+ |if (a1 > a2) ;
    / w* _* Y9 B7 q' W% Q  c5 Hif (a2 = a2) ;; r$ P' Z& x$ H
    if (a1
      |) A, p/ b0 L7 K5 k; ]1 r支持整数的自增操作符:
    $ g5 F: u4 r9 ?2 O0 R( y7 Uint k = 100;+ W0 h+ s2 r' Z0 o+ g. A. `
    for (int j = 0; j
    & c' X$ f8 N4 B3 r; g& J8 Y4 VC# 浮点类型的支持
    ) ?0 m2 a; i/ I不支持
    ; q4 ?" [9 M2 v8 `' y3 v4 zC# bool 类型的支持
    % \; {" k! ]0 |; R5 \( _8 q基本支持,底层行为和 INT 类似,false 为 int 0。( v) K/ z; C! V/ a4 y  x% {
    C# char string 类型的支持" I! i" V/ ]* Z* s. ]
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。. o1 c' ?; _$ k+ V9 F& {
    尤其不要使用 string 处理中文。4 q$ {: ?* w4 M+ K
    string ss3 = "ab";
    ' ^) Y/ b- l- r/ J3 pss3 += "c";
      i$ I/ i0 L( `) ^; lvar ss = "abcdef";0 G; Z' n: v5 @/ y
    var b2 = ss.Length;1 q  B# a+ y0 V% k
    var c = ss + "abc";
    2 H  N! o1 ]9 ?8 Uvar d = ss.Substring(1, 2);
    9 ^3 q5 {7 a- r( |6 u0 _9 N6 d支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。; ]& l8 ~6 r$ ]" T! a9 z
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。3 @2 _- t# l4 t# N5 X
    char 类型作为整数支持。& D0 @* ?% Q  T0 B! c( \/ p  C( f4 w
    C# class 和 结构体的支持+ S; ?# Z0 O3 X6 u- B7 P+ f
    支持 C# class 和结构体定义。" v5 [7 I( X2 N$ W$ Q$ D
    public class info
    $ S& h' b1 _* S" q" o: R  v8 ?0 T% N{' }: t- {  L* m
        public byte[] a;2 t; [; K; m9 z  n$ W- W
        public byte[] b;
    & l0 T% v5 m2 k# |9 U}
    * @& r! q* Z+ R2 H, _不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。4 y& k3 {9 B* _% Q& k' o
    不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。
    5 |( N2 ^8 n% XC# 数组的支持
    . }+ E) T+ A4 j) C# T- b/ M& ~- U数组支持,行为基本和 C# 一致。9 [; W, R; H) q/ ^1 H' L
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    " S" x. b- h  U1 V$ L- B8 ~对一般数组可以用的设置其中的值的操作) u" y' t  S' h) P. a. z
    short[] some = new short[17];) @' R) J( M; K6 n" P; m
    some[1] = 12;6 O, c, }9 v8 \; P; a- s
    return some;
    3 a1 f1 J- R! e6 [+ {7 k对 Byte[] 不允许。" P0 n4 P: @8 A' s( [( E& \4 q
    C# 枚举的支持, A# b' A: M# M4 u- i2 _. B
    支持仅作为数值使用时定义枚举。8 ]$ ^0 z( ?3 z" [4 `
    不支持格式化为 String,以及从 String 解析等。6 v( K' X" x8 T5 L* C3 C. ]- ]% `
    C# 容器的支持7 p4 a5 q+ I9 K1 U+ V: Z
    不支持 C# 常用的 LIST Dictionary 容器。$ x; C4 W) }% @# w9 G0 O! \4 u
    LIST 功能可以用数组替代。: j* J- a  A! p# {, p. ?
    Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    . k7 ?1 `5 E6 [9 }2 j( M- ]( rC# 变量的支持
    ; d" a( v& O" P5 Y临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。
    + V1 s6 r  s/ g6 Jprivate const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;! }/ `+ j3 v( M! n5 ~1 H
    public static BigInteger TotalIcoNeo() => total_neo;2 n6 U' N; T' t+ x2 T, }
    C# 委托和事件的支持
    , U. |2 b; n" [8 P  S) g' l+ M( AC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。2 V- T5 P+ d) c! n3 d
    public delegate void acall(string a);
    0 E1 k# S0 r# B2 f% \% C% u% f一是可以用来定义事件:! [+ |! i5 L/ e! c. w" s3 l
    public static event acall dododo;7 |8 |$ E, W3 E4 E2 J8 X3 I
    调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。, b5 t- J. R5 m8 U" y0 m
    另一个是可以将一个 bytearray 转型为一个委托:
    1 W* {6 o) r& q7 {acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();
    ) \5 E; A/ P* p1 q2 g这就实现了对一个指定地址的智能合约的调用,参考 NEP4。; L1 z7 q0 G& H. m) J+ W
    C# 开发约定
    6 |8 X9 a/ p/ Q) W8 C0 ZC# 的导出要求1 l' M1 Z' E& B" R
    NEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    & R& I0 ]* y* v$ O% n1 T其它要导出的函数都应该为 public static,且不可重名。
    . Y  p. y6 B. e6 o. K2 i4 EC# 的委托和定义6 y  t# J& I( Z0 z
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。; ?7 u) _5 D, y4 V. m- u
    分别对应 NEO 智能合约的通知与 NEP4。; K/ q8 g6 c" L/ z* P
    内置特性
    6 `" A" K' a3 Q如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。6 v' U9 }  T* ?# ^( f; j' r. q
    也可以在你的智能合约代码中使用这些功能。8 Y0 N, C# p3 x7 B" Y1 [4 T9 z! s7 x
    APPCALL特性. Z' V$ z& j/ [$ B, b0 F
    调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。; p! }: y% J- P# [. e
    [Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
    2 M, L/ F) A! e9 X/ u$ @public static extern bool AnotherContract(string arg, object[] args);
    9 m9 O* b. [- X5 N+ p9 X' \SYSCALL特性
    , i- _5 V3 \; s% |4 S& |: ?调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    / X* b  Z+ ~8 D, E, o[Syscall("Neo.Account.GetBalance")]7 ~- l( _# s# W6 k+ x7 b
    public extern long GetBalance(byte[] asset_id);; \: B: E9 V9 U8 n
    OPCALL 特性  p! C0 I( ]+ w* T; o0 ^3 `
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:
    2 d$ H0 x1 |/ {% J[OpCode(Neo.VM.OpCode.LEFT)]( `8 ~& K8 \7 J9 G0 J
    public extern static byte[] Take(byte[] good, int index);" u9 J; e/ P% \+ }
    NONEMIT 特性5 a- b4 ]2 U/ q3 {2 t8 [! k0 A
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。6 k4 |# Z  w0 H* j+ n) Y( l0 k; y
    [Nonemit]# h( C! N$ v9 v4 `. C+ ?! n
    public extern static Delegate ToDelegate(this byte[] source);
    2 J# r" J( f2 `( yNonemitWithConvert 特性
    ) n  U8 o9 a! H$ @2 z; G, p当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。
    ) t0 T5 b- K$ ]. j3 U[NonemitWithConvert(ConvertMethod.ToScriptHash)]. c% C, b1 F: s) J' t" Y$ N
    public extern static byte[] ToScriptHash(this string address);0 q6 e+ i; ^  q* S
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。" S" T- Z* v6 ~$ \
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11