Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
120 0 0
合约编写须知
- T& `( o7 _/ ~) A' iNEO 编译器支持的 C# 特性
5 Z+ F' J# G) ^+ P- Z$ }使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。) p5 C. d) y2 J: p7 _3 G
因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。: C! `+ r( S% `! q; v2 G
关于类型
* J/ p6 q. l, U  pNeoVM 有几种基本类型:# k+ }+ ~! f8 d  n
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    ( r8 {5 T- |. K; ^& @9 m; F
    / O! W& s$ r8 z5 W/ O; W- w; [, Z
    而从 AVM 代码中能直接产生的基本类型只有:
    : ^9 t5 _6 i. n" ~4 g
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map
    0 w$ O+ i, w# E# R: R* Q* w  D
    ; H, f; w; ^2 l+ o2 B% b
    C# 的基本类型有; \& ^0 u$ @+ k* m+ |4 W- X5 D7 O) g% B
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String" z- \  ?8 M/ }2 ?, N
    4 z& u1 L& g0 g5 c& f1 E
    因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。5 y) e& y# B( u! b
    C# 整数类型的支持7 W8 K8 ~6 J9 n% z* N. ?
    Int8 int16 int32 int64 uint8 uint16 uint32 uint64
    2 i# y2 _. v& W8 ]7 s, S; ?6 L因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    ; H( ^3 a* w: m/ k: V. O/ \( C一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
    . w# ?7 _2 E. w" X( ]2 y1 M- f, h另外对 C# 的 BigInteger 也可以支持:' Z% O' [% ~7 y
    ulong total_neo = 200;
    # D- k- e1 O- n2 O9 Y4 `7 _BigInteger ico_neo = 300;
    ; }" x/ J* B- V" e! t5 {BigInteger balance_neo = total_neo - ico_neo;
    0 N( l1 `( h  f) W. sulong value = 150;4 D& l7 [5 |. l$ G6 h
    需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)+ n8 ?6 v* u% m7 O* T; B7 U8 R% _* e
    对所有整数类型支持数学运算符 + - * / % 加减乘除余:% ^# `! E/ u( c5 g' {0 @" Z2 I  r
    var a1 = abc + 1;
    0 _/ r& m0 B% c: ]( Dvar a2 = abc - 1;* q; ~9 a* Y; \
    var a3 = abc * 1;1 Q9 X3 P5 u0 N4 s
    var a4 = abc / 1;
    5 y/ V2 ^3 b# V9 K' g9 hvar a5 = abc % 2;- T0 K  x( o2 e3 k
    对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:6 i; @# d# [  ^. G
    if (a1 > a2) ;" u$ H: c: `$ p3 H4 D
    if (a2 = a2) ;
    % \- A/ U8 [6 U. O6 o! E9 r; `; rif (a1 * a, i* X( [* ]6 a
    支持整数的自增操作符:
    7 _$ F7 T: O, ]# fint k = 100;/ G: f4 o' G9 y1 `
    for (int j = 0; j 4 l7 |0 X  J! W
    C# 浮点类型的支持
    ! Q8 D9 |% h6 l7 c6 A! i4 R不支持' N/ n' x* R6 c0 r8 y0 Q  p
    C# bool 类型的支持4 t3 G2 H; u# f8 _' p
    基本支持,底层行为和 INT 类似,false 为 int 0。
    ) t5 k' [- O* v5 G. xC# char string 类型的支持" e# `3 `! h- j/ i% M" B! m
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。/ b' z% s. G) ]$ d7 j
    尤其不要使用 string 处理中文。
    : Y" d" r( e- u7 \9 {! ~0 Ystring ss3 = "ab";; L; a& W, O9 w* M; J% o
    ss3 += "c";, M/ N" u$ P0 M8 U
    var ss = "abcdef";
    3 `. R# e' O# u. ]var b2 = ss.Length;4 y. o  b: |* }  a
    var c = ss + "abc";/ n* p* y0 V! [% S2 t/ E
    var d = ss.Substring(1, 2);
      ^: ?; v4 T. J3 P4 U& `  x支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。! Z# a9 A5 g3 L* y: `. v9 j5 L
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。. }0 T! v8 V3 j+ m. E( `0 M
    char 类型作为整数支持。
    6 r8 p3 y, V# ?+ B' wC# class 和 结构体的支持
    7 R& ^5 ~6 U9 U! j6 O+ b$ d支持 C# class 和结构体定义。0 `# F1 S9 T0 u- r3 b
    public class info
    7 y: P0 s+ d* W' l! b1 Q! B* @{  {9 Y, |& |6 \7 R/ g3 A* n
        public byte[] a;+ ~9 w" c1 Z: k" X/ L* U
        public byte[] b;
    5 v" O% o1 t: _! t& o}) @! [/ x$ T) O4 k
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    1 @' z8 w% i% x" z! u) o, L. n1 q不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。% t! F2 E5 n7 b: |
    C# 数组的支持" i, F( ~8 a, A2 X7 N; Z6 m
    数组支持,行为基本和 C# 一致。+ w7 J" f% s) W# {1 H; L
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。& j  S6 e* j6 C
    对一般数组可以用的设置其中的值的操作
    ! U) x& Y6 D9 b6 yshort[] some = new short[17];2 k2 ?3 W" }1 @2 {5 t
    some[1] = 12;
    ! }: X6 C0 p; E5 Yreturn some;$ K6 [0 |$ @; }; G9 f9 n8 z
    对 Byte[] 不允许。! H- i( W/ U. W9 ^3 B
    C# 枚举的支持
    8 A& X, @) S+ U8 U: z, Y支持仅作为数值使用时定义枚举。
    , c2 ^3 d. i% w5 a9 r, A/ t8 w不支持格式化为 String,以及从 String 解析等。
    3 @1 x1 Y$ j( ?/ X2 x! \C# 容器的支持$ f: d! h1 L1 e7 v
    不支持 C# 常用的 LIST Dictionary 容器。' F* ~- \+ G* D) ^) W
    LIST 功能可以用数组替代。
    / n2 e. E( F# T8 ~/ c. @% U+ S0 ^Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。. r2 A& a, p( [! f
    C# 变量的支持
    8 h7 ?) q( \' [. ?/ ^9 b临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。
    & _. j$ _$ }+ q- P* Cprivate const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
    2 n1 s# W8 i" J) h* q: rpublic static BigInteger TotalIcoNeo() => total_neo;/ M' C" A4 R" m
    C# 委托和事件的支持# c% {/ F) [! w( o* X1 m& L
    C# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
    1 G; P5 g: ?/ Q6 ]public delegate void acall(string a);7 |5 J$ B" w: f; z
    一是可以用来定义事件:
    * Y3 O: k" \$ h$ }: r1 Tpublic static event acall dododo;
    . Y0 p) I% U! v调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。& p( r  {6 D( x+ ]+ Q" h4 G) o
    另一个是可以将一个 bytearray 转型为一个委托:4 ?" e! v8 {1 i  f  M
    acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();; [9 y5 {$ t- g" u
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。$ O5 a4 a* d) t! C. c
    C# 开发约定
    . M  T& B/ o" G/ O, i1 E7 b& ^C# 的导出要求
    " [: e4 z- j2 S6 E* p# ENEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    2 N+ }% N/ n) ~9 z" D3 g( {其它要导出的函数都应该为 public static,且不可重名。
    2 i; P( g! A; u' Y3 N- sC# 的委托和定义2 C/ r0 D( ~4 Y1 @& x* Q3 ^( ?0 F
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。. p8 m# F" o& i) X9 o
    分别对应 NEO 智能合约的通知与 NEP4。3 t+ i' y" D# \& ?. s) H
    内置特性
    - p) a5 p- F/ l如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    * B) o1 I7 {2 s也可以在你的智能合约代码中使用这些功能。0 r2 u+ `/ u( r
    APPCALL特性0 I+ t" G+ ], ^+ |; n9 k, L, `
    调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。: U2 r9 ?0 u3 [& O3 n7 X6 m
    [Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]# @' B4 G6 W6 `9 q3 L* I& W0 X
    public static extern bool AnotherContract(string arg, object[] args);, v5 w5 A% m* |6 r
    SYSCALL特性
    # I% d* [# _9 z1 Q. E$ k- o1 y( J" n调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:: g2 M2 c4 \+ s
    [Syscall("Neo.Account.GetBalance")]
    1 X( N; i% W6 j3 g8 Npublic extern long GetBalance(byte[] asset_id);* ]. Y  Q% Y7 \' J
    OPCALL 特性6 l+ h! b1 J" b+ `; F, W4 |! \- W
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:9 E9 [5 D( A& v  k* V" y$ `. w; A. o
    [OpCode(Neo.VM.OpCode.LEFT)]5 Q: X. I  ]$ b  J
    public extern static byte[] Take(byte[] good, int index);0 y; s+ c$ f6 T( Z, g
    NONEMIT 特性
    6 |' p4 ~5 @; u5 D% e& I执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。4 n6 |  g! E- h8 G( O( g" ?- U: L
    [Nonemit]
    , h9 n. I( c( C( X0 dpublic extern static Delegate ToDelegate(this byte[] source);$ W, D; @# T9 k2 K4 j9 B
    NonemitWithConvert 特性
      d8 J" a1 ^* r当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。- J+ R9 K5 v4 S( i8 H* k* R
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]
    " b* j. u3 v6 ?& l' [  u, {public extern static byte[] ToScriptHash(this string address);
    - m# ~; t) W' a例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。6 Q. Y! G" V1 }4 {$ `" _9 H
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11