Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
211 0 0
合约编写须知6 c- c+ Z+ y* B3 @1 \0 U9 b
NEO 编译器支持的 C# 特性
3 w% `$ ~, O& p! ]! j8 o* j- S) ^使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
% z7 e& w7 m7 o. ~- b5 V' U因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。2 i/ @: @+ p9 a. r
关于类型
* F( I. j- o$ I) A2 D  |/ BNeoVM 有几种基本类型:! S' j) \5 f, u$ c9 }# N$ c
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    4 q( t; G5 K& \* p# Z) T5 a

    6 C3 ]: ~# L* y$ _! x# q- E" X而从 AVM 代码中能直接产生的基本类型只有:( d4 t) L, f& a( R! s6 Z1 x' D
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map: G. N9 b. q* {
      s; ^8 \0 ^  [$ J' U
    C# 的基本类型有
      T% j% M+ p+ h1 d: c, L8 C
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String
      i- g* d2 W  ?& k

    : d. Q& h4 y4 V2 l8 r  {' w5 F. a因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。! a# ?1 m* }( a6 U6 C- _' G: b
    C# 整数类型的支持
    5 y4 f- Z$ O# d0 S7 \2 [( G; w$ PInt8 int16 int32 int64 uint8 uint16 uint32 uint64# z  `) Q3 z& e* m# G1 R
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。. l4 I+ E4 [+ y0 Y
    一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。' A: W* F5 _9 S% }6 o0 a
    另外对 C# 的 BigInteger 也可以支持:( n  v# L# N, {) u( X  @
    ulong total_neo = 200;
    , ^0 q5 e) ^3 {7 i5 w, S" tBigInteger ico_neo = 300;
    * {1 x7 e7 r% eBigInteger balance_neo = total_neo - ico_neo;
    9 |2 I, A# F) y2 G6 Bulong value = 150;6 g4 X& t6 l. X! R2 f
    需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    * }2 h: e* B) X对所有整数类型支持数学运算符 + - * / % 加减乘除余:) A6 V9 x  j, g! U, w4 H
    var a1 = abc + 1;
    , B% p8 Y7 X) ~1 `var a2 = abc - 1;; `: R, o  `* j  |, H+ c+ ?* Q* v  C
    var a3 = abc * 1;' z7 K2 h% n: ]
    var a4 = abc / 1;
    " U9 c; k+ i: I( K3 _var a5 = abc % 2;) n( J, T4 O6 D% }) K% _
    对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:) ]! u7 Y2 u1 [! a; D% h) M
    if (a1 > a2) ;
    0 x6 z" _+ q. |; G  aif (a2 = a2) ;6 ?6 r$ q4 H9 ~/ `4 n5 t
    if (a1
    : C& e: i4 z1 z) r. M4 I% \支持整数的自增操作符:; O9 I$ @' I& l
    int k = 100;
    . \  J. i3 `: K0 ~9 X. Bfor (int j = 0; j 8 ~' D* p  M) b# ^! Q, n5 E
    C# 浮点类型的支持
    * R4 }/ d) ~  ?. _8 }不支持( |. L, o# v/ ]8 Z4 m
    C# bool 类型的支持/ |: M! ~* M* W/ S: O1 \  G$ N
    基本支持,底层行为和 INT 类似,false 为 int 0。
    $ X" T. g8 i7 W; _: hC# char string 类型的支持1 b) U9 I5 M! _, v! W3 K& O1 J
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。
    " E" w' U6 A) f5 s8 K尤其不要使用 string 处理中文。4 B5 ^1 z3 g0 w- S
    string ss3 = "ab";4 {+ ?) O& s+ a% }) X- Z6 a5 T
    ss3 += "c";
    % T6 S3 W2 h* j. B( L, fvar ss = "abcdef";: J, `/ V) [9 r2 S# Q1 c+ v
    var b2 = ss.Length;
    . B7 @- t1 }- z# N# x  o, |2 Xvar c = ss + "abc";
    : z9 t$ L/ f; m& E) {! Nvar d = ss.Substring(1, 2);6 U; d9 }1 b8 d5 y+ ^/ \( B
    支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。; A3 ]8 Q: X5 A0 a* Y8 e: _
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。; A2 ^) `' b8 y  x9 P
    char 类型作为整数支持。
    5 m0 n+ s' a1 E& P; |; JC# class 和 结构体的支持. W& T- v+ t+ Q& h5 N( z
    支持 C# class 和结构体定义。
    & v* _7 g/ C* N8 d% L% V3 Opublic class info  a  Y7 e* J( M; ]) d0 u7 [( N, O
    {4 z9 j7 W+ G' Z( l1 Y7 `
        public byte[] a;
    * ?3 T! ]+ m1 d, f( ^0 ~% H; E3 |    public byte[] b;
    * u3 x  f# f9 M( U3 e: g+ ^) C, ]( d}* q. m/ C' K, T/ N
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。8 Y( U8 h. j, J* ]/ o/ f" M# t
    不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。
      _8 p1 V) V, ]% ]' b! l" N7 g" q' O+ y$ gC# 数组的支持7 A) C' a9 J/ S( @6 L+ A" T# Y
    数组支持,行为基本和 C# 一致。6 w% j, ]; R7 y1 v
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。$ w/ Y4 Q4 ?4 s* {  D+ C5 @
    对一般数组可以用的设置其中的值的操作
    , P! b. k: w' j% t- t" Q7 hshort[] some = new short[17];: f' k' [4 u: }6 T6 p# ^9 g, _
    some[1] = 12;
    & e: R/ n" N+ preturn some;
    # T5 g7 n5 r1 p& H9 o! U2 X+ i对 Byte[] 不允许。% x4 K2 W% V4 r$ w
    C# 枚举的支持
    4 X. O0 Z' i- g6 P* i支持仅作为数值使用时定义枚举。
    5 v- R  ?. B. ^6 A不支持格式化为 String,以及从 String 解析等。3 L& W0 M. G. o  D
    C# 容器的支持: A# d- l6 U% d& H4 ^4 T# T; L9 R
    不支持 C# 常用的 LIST Dictionary 容器。' J1 x2 X9 V2 m$ X' ?
    LIST 功能可以用数组替代。
    0 x' P# V! E! UDictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    6 x- ]8 A* [( U- P% VC# 变量的支持; g- X+ M& m- a: x+ W! y
    临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。" ^) D6 a; F# g8 O
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;$ _' H* V  k% \
    public static BigInteger TotalIcoNeo() => total_neo;* \9 d( m0 x+ u$ m  B
    C# 委托和事件的支持
    ) [( D; h8 z" K. sC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。& Q0 ]2 L2 V5 W9 @5 |* D
    public delegate void acall(string a);' P6 G4 e+ b" |1 B- _5 R# ]
    一是可以用来定义事件:
    ! o" p: j, R7 B# N# i( z- u$ I1 dpublic static event acall dododo;
    3 ?, v6 V0 }. W. b5 Q. E3 ~调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。
    % B' U8 r7 L  O4 R4 E另一个是可以将一个 bytearray 转型为一个委托:$ |1 s1 ~. Z7 g8 p8 y' g* D; U4 J
    acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();. B& A) C; k2 I" \# n- K( V0 t' Z
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。
    ' b* d* W; u- n6 ZC# 开发约定
    2 h- z9 a: ?+ ^" nC# 的导出要求
    9 I- Q: e0 k. VNEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    ) H6 ^: I5 C) n' _5 `  p其它要导出的函数都应该为 public static,且不可重名。
    ( T1 b$ n5 M& ^& E, ~2 Z* R% _0 G# _C# 的委托和定义/ ^0 M) g) Y) t5 q% q
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。* {1 J) ~$ w- |% ]
    分别对应 NEO 智能合约的通知与 NEP4。
    0 a6 G5 H5 x: P5 \, y6 p( `内置特性
    # `! Y" d) E+ h- s+ Y  @如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。: M+ x; ]# c* K/ w, I3 I
    也可以在你的智能合约代码中使用这些功能。* Q$ G* q* Y8 a6 \. b
    APPCALL特性
    0 W6 }' ^9 p1 S- F调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。% [0 q# i1 M( A- |7 M* O2 s- [, L
    [Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]% X6 l. W$ l0 M- J: V( e: V7 B
    public static extern bool AnotherContract(string arg, object[] args);( E8 P, w5 q; p% f8 o3 p) L
    SYSCALL特性
    ! H" U* t7 v5 C4 \! G调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    : ~# y- L1 y/ `$ ^" G% {[Syscall("Neo.Account.GetBalance")]
    6 m0 p  n! ]2 P% V# w# @0 n4 upublic extern long GetBalance(byte[] asset_id);
    0 e# r1 x$ P; g1 ~. F! r0 ^2 KOPCALL 特性7 j( L9 M' J2 M$ [0 R! h1 |$ U
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:
    : Y* r% }8 y, Z5 e[OpCode(Neo.VM.OpCode.LEFT)]
    8 O' a$ Q- g$ ]& k1 {& A) npublic extern static byte[] Take(byte[] good, int index);
    : t) l. P! E1 H4 ]) c) WNONEMIT 特性
    . |) h4 k& |+ x" P; ?2 o# B% i* ^执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。; M( E, u% d$ |6 z3 D9 u6 x; I
    [Nonemit]
    - p' K+ d2 I' M0 V9 i+ s6 s2 Mpublic extern static Delegate ToDelegate(this byte[] source);
    % f! M, _4 }/ E, P% INonemitWithConvert 特性
    * ?+ Z6 s2 Q; \: z& i2 ]' J当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。6 w" ~( X! Q/ Y  q' N7 p7 `
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]) `/ i3 t1 B3 e# c! }
    public extern static byte[] ToScriptHash(this string address);
    . y* y5 R: P. p; p( p" t4 ~) z例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。( Z* h' r- u$ S3 t+ _
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11