Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
88 0 0
合约编写须知4 G6 c7 E" Z7 c$ |
NEO 编译器支持的 C# 特性6 U: Y& ]: M6 X3 w1 r  M
使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
, ^' V. d, K7 c, _3 Y因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
) Y, N3 m# }# H. ?( i( c关于类型2 b" t  e5 B  `
NeoVM 有几种基本类型:
$ E) x2 \2 {$ a" t1 ~. t/ h0 _
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    % ]4 q8 X* _& `! Q
    / M' S  o, I: p( h% {1 M
    而从 AVM 代码中能直接产生的基本类型只有:
    ; g* W% A% c: J- V$ [6 N
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map
    1 ]2 Q# c" D8 ?( h9 q3 R
    - _3 B! x; z- M+ J; x, Z* W/ u
    C# 的基本类型有0 o' c2 R6 K: |9 X4 O6 P
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String
    3 J9 T  R. U5 @3 F) J7 f/ \
    0 D4 ?: u1 L4 l/ m" k
    因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。
    # n( A7 N& ^5 H2 uC# 整数类型的支持
    ; R' S+ Y5 b, \& B7 \Int8 int16 int32 int64 uint8 uint16 uint32 uint645 [, F: M. Q' Z6 `
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    2 u7 e4 g* w. B! e8 o/ }% e9 d; g9 {一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
      R7 D1 O' _; j" {' N另外对 C# 的 BigInteger 也可以支持:
    . V7 g0 F; B# m3 W/ _; I0 `% sulong total_neo = 200;2 i1 ?. T- x* X% {9 F  C- z
    BigInteger ico_neo = 300;0 L( B8 P1 M* S" C6 ?+ q
    BigInteger balance_neo = total_neo - ico_neo;
    ; n& L1 y* m$ d6 v" zulong value = 150;
    1 g( Q" t5 |& ?, }+ I需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)" d% \0 m! V* K7 i2 c
    对所有整数类型支持数学运算符 + - * / % 加减乘除余:, [6 W5 U6 `7 ^
    var a1 = abc + 1;
    3 V: k2 j8 t+ k7 pvar a2 = abc - 1;
    1 m7 e; Q) ]) `) F0 {/ v; Rvar a3 = abc * 1;6 k# G) w% H9 d6 I8 m: Y
    var a4 = abc / 1;2 I) s; j/ R4 s* I9 H9 z( M
    var a5 = abc % 2;
    : A1 |9 l* N  P  R7 ~对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    9 r5 ~0 q* o# s' |. V. i8 B! kif (a1 > a2) ;
    / I# J" S; l) h, }) f( @  Kif (a2 = a2) ;
    ( T# |9 |* u- B. s2 Pif (a1
    9 Z  t2 H) A9 ^! h8 I4 |6 x6 j支持整数的自增操作符:" n8 v% O( W" [& }
    int k = 100;3 R" d- }1 q% I2 N8 {. e" w' `
    for (int j = 0; j , |+ L' l3 |3 g0 ?7 @8 ]! M
    C# 浮点类型的支持
    & g: `" q; i9 x$ G7 ]不支持
    5 M8 q+ f4 ?, S/ Y7 C: U* uC# bool 类型的支持
    : ^3 z; c. u4 y/ Y  Y基本支持,底层行为和 INT 类似,false 为 int 0。* y7 M8 B. l3 j; I
    C# char string 类型的支持
    & s9 ]* S9 @  t不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。
    . B& i* G+ {2 B9 j9 n6 H尤其不要使用 string 处理中文。1 x; s) Q) j4 E/ J) l' A" b3 J
    string ss3 = "ab";
    % c0 R& ^9 t3 j( R8 A& N7 L$ ~& vss3 += "c";
    8 g! c5 T; e9 @% {6 }var ss = "abcdef";+ Z( F! o3 m3 k6 t4 ]! v/ m
    var b2 = ss.Length;
    4 N: N0 I0 d, u, V4 Fvar c = ss + "abc";
    6 T. x  j- d8 g# {3 a' h- z& Yvar d = ss.Substring(1, 2);
    ( Y: b5 f# o' J- |) ^, l% B7 a: r支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。
    3 v) B$ `' w, q1 @另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。7 H3 ]9 N9 h$ |8 F! o0 l% Z% I6 n
    char 类型作为整数支持。
    1 @$ {: U, m0 E; {3 }C# class 和 结构体的支持4 W6 H) E7 Q, u" E) v
    支持 C# class 和结构体定义。9 v  }0 v5 T( b7 P# [$ \
    public class info- ^4 a! F% K) |3 G
    {
    9 U7 [! F" y7 ?    public byte[] a;
    ) ^" y8 J$ i9 |    public byte[] b;5 g" r$ C  ^' z" b: h. W/ Q% p
    }/ |% o% b& U6 l$ h: c8 P# m8 p
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    . ~# H# a% f9 A# k不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。0 m* H5 j6 c& G) e7 n" U- Q( x
    C# 数组的支持
    & u8 i3 z" U' J, _8 W3 [9 D; k数组支持,行为基本和 C# 一致。
    - W( \5 u% Q6 q: H! d2 \& b& YByte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    & m# ~' q+ I3 v$ T" B' u对一般数组可以用的设置其中的值的操作
    3 m8 O+ {: w- \# X4 o7 Rshort[] some = new short[17];) K4 ~% K+ ~# K; v* B
    some[1] = 12;8 s: }$ e7 f, a5 O
    return some;
    $ a" x8 _, Y. C" e) y% t对 Byte[] 不允许。
    ! Q) h  Z" w6 ~! ~* I9 I6 JC# 枚举的支持
    . r" x5 C/ P: b) v. r8 e支持仅作为数值使用时定义枚举。. B7 ^  t( Q! [9 c
    不支持格式化为 String,以及从 String 解析等。
    / G- U' t7 F& WC# 容器的支持
    6 z: C% j# a' V不支持 C# 常用的 LIST Dictionary 容器。6 O0 n9 A1 _( t7 g# A
    LIST 功能可以用数组替代。
    . N8 h( q" r5 d, z% ~- nDictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    5 c) Z- X, y, ~5 K- jC# 变量的支持0 ?2 r1 _- J: y( z
    临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。. u9 t- i5 n/ m1 |  I' j
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;4 I" I5 y. X; v- S
    public static BigInteger TotalIcoNeo() => total_neo;6 c' I% `# ~% }3 T6 z3 D
    C# 委托和事件的支持+ O) C3 m  v; ?9 c) b; }
    C# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。$ K; L4 E: J! @7 h3 K
    public delegate void acall(string a);
    " S9 [# C9 d1 Z; j& n5 _一是可以用来定义事件:
    ; d4 G# b# Q/ V- _" }" y* C) @' b' \public static event acall dododo;
    2 V/ w, T+ Q( z; A: {& X调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。1 a0 @/ x3 O, o7 y- A) [2 I5 n0 U
    另一个是可以将一个 bytearray 转型为一个委托:' x  }& o* X4 ^5 C
    acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();
    5 L# Y8 e2 A% Y: f. L! e0 u这就实现了对一个指定地址的智能合约的调用,参考 NEP4。
    % K* Y; t$ O8 MC# 开发约定
    3 M! `0 Y; U" s9 f/ |C# 的导出要求
    $ x$ _! {3 Q, U7 Q' U" KNEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    / ]0 i5 O) F  ]5 L- r( |5 l' N其它要导出的函数都应该为 public static,且不可重名。3 E2 P" w& t. }; ^$ e4 p
    C# 的委托和定义2 c! B: h( @# P! j  {. S
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。5 K$ E- z* k9 @0 `. E
    分别对应 NEO 智能合约的通知与 NEP4。/ l% O. S* }! w. V4 o
    内置特性+ M( \& q) M4 r# ^- h
    如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    ( j+ ?6 z0 F; \+ `) _, l也可以在你的智能合约代码中使用这些功能。! {& d3 T  Q: O; H
    APPCALL特性
    ; [  o8 X0 {: T$ P4 O调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。
    , l- R+ l$ H2 s, |[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]+ `4 R. h! ^/ h( U- {# g6 X1 `  e
    public static extern bool AnotherContract(string arg, object[] args);7 n& `& ^% w4 |$ o/ b! S7 c
    SYSCALL特性3 e  i. K+ w9 v% v( j
    调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:6 S: x. I# t( S' X2 e% y( F$ m
    [Syscall("Neo.Account.GetBalance")]" K) Q7 T6 ]% S& |; v& J+ ?
    public extern long GetBalance(byte[] asset_id);
    9 G' S$ A. i2 u) jOPCALL 特性' _' a* P5 b; w
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:! {( W& W* v( ^9 h2 C9 y% _
    [OpCode(Neo.VM.OpCode.LEFT)]
    ( E/ p% b" |, m4 bpublic extern static byte[] Take(byte[] good, int index);. x: x# N* w5 f- {. f( b, ~: F
    NONEMIT 特性' V2 w) y0 u2 f; ?! V. ]
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。5 L3 j3 U8 a/ D! h) ?* _
    [Nonemit]- k8 i' `$ n3 z! `1 K4 X
    public extern static Delegate ToDelegate(this byte[] source);
    : T% f  ~8 e/ c5 H1 l, m1 bNonemitWithConvert 特性
      O9 J1 b5 N' ]; C4 F! }' H- G- h当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。& J3 o/ V, a6 {
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]
    ; D! B- z; M* R6 T. |public extern static byte[] ToScriptHash(this string address);( o$ Q0 b! E1 Z
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。/ _9 E. s* A& U& B0 F  k3 Q+ f& {
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11