Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
83 0 0
合约编写须知) u/ v3 g. j8 n2 t& |; a
NEO 编译器支持的 C# 特性
' E7 H) p& o# B2 x7 {8 G/ e使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。- D2 `5 A. J6 [2 q, S
因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
- `0 L! k0 j3 I; t+ i: M关于类型
+ @: Y) p0 b8 k7 o; p3 F) f* f# G# D: pNeoVM 有几种基本类型:8 X: r# Z* [. S1 ~0 ~3 {3 J
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface1 _4 s8 v5 n9 o, m
    5 B3 [& B4 r% m6 k; q4 j& R0 H
    而从 AVM 代码中能直接产生的基本类型只有:: L* H1 @" ~# F' j+ s8 B
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map. I, C* }; b8 h, I4 ?: h) c/ v  n
    0 |7 Q6 k+ D; i  t! D
    C# 的基本类型有8 Q+ n( e0 V- ^" Q$ L5 Y( T% H
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String# ~9 D* Z2 s$ m" d6 t

    + ?* c$ B  a, P9 q  A! w因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。
      D0 @8 B" @+ S) SC# 整数类型的支持7 B3 @0 ^6 @& Q0 H4 H  P7 J. t
    Int8 int16 int32 int64 uint8 uint16 uint32 uint64
    + Z6 H/ P$ @1 u4 _因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    - z4 X& D4 T6 m1 X2 [% v* j& p一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
    & y0 w' K, z- A( I4 u另外对 C# 的 BigInteger 也可以支持:3 I) Q8 }/ `- E) A. W1 ?
    ulong total_neo = 200;- p* E0 H4 T1 i' p  F
    BigInteger ico_neo = 300;
    ; j" X: n* ]" e5 c3 Y' [BigInteger balance_neo = total_neo - ico_neo;# H! h8 ^: {3 ]
    ulong value = 150;% A$ p  R8 O  ^6 }1 T, E
    需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    6 h& E$ K+ ~( h7 p* _& i! P# r9 }对所有整数类型支持数学运算符 + - * / % 加减乘除余:
    & `; ~2 e. X$ zvar a1 = abc + 1;
    : E# l. J% v" x! K3 q$ svar a2 = abc - 1;) b  W: P' g9 `% `* N
    var a3 = abc * 1;
    , O% s# ^4 O0 m' ivar a4 = abc / 1;
    & g6 j3 n/ Z) p0 {. S) S# c& ?var a5 = abc % 2;
    , V7 V" M9 i3 X+ W  k. c对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    ' V3 _5 A# d  s$ }/ aif (a1 > a2) ;7 U/ A1 ?  [7 i
    if (a2 = a2) ;
    9 T% C& _4 @' q' cif (a1 6 M1 d$ k2 O" A8 h; M  t; @. }6 K
    支持整数的自增操作符:
    " L! G9 |: K3 r& Kint k = 100;$ x: ?5 x$ D/ x0 z. _5 ?
    for (int j = 0; j 4 }* o3 I/ ~- e, c6 \
    C# 浮点类型的支持
    , F0 o7 |3 T7 Y' C. z- l不支持
    % _) P' X1 k* d5 \/ fC# bool 类型的支持0 _- ?9 H& {9 u( V
    基本支持,底层行为和 INT 类似,false 为 int 0。2 g# E  N9 Z' U5 \6 I9 G
    C# char string 类型的支持
      z1 r6 X+ A3 q( N4 S# V# @不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。
    , g) [9 x# H! o2 N  L* V" y+ `尤其不要使用 string 处理中文。5 [5 y% d, V# i3 C) i
    string ss3 = "ab";
    8 W9 I% x% `* L& m" {ss3 += "c";; V/ }7 u- d2 R% Q
    var ss = "abcdef";. j* w5 I5 Q; Z7 ~, L
    var b2 = ss.Length;
    , J- J/ P& Q8 \/ v# Pvar c = ss + "abc";* o8 b# A& _) h7 J' k# L7 |) d9 m
    var d = ss.Substring(1, 2);+ n3 e' H; J- R3 F1 `! @  p
    支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。4 O  o$ U7 z% D+ |: c- h
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。
    6 J: D5 c, w, x3 S* h! Rchar 类型作为整数支持。. N4 j9 y2 \5 |: Q# I/ t* Z
    C# class 和 结构体的支持7 [: ]* W* ?8 X5 F! p+ D/ h
    支持 C# class 和结构体定义。
    . Q  ~2 h; l" d/ kpublic class info4 X7 x& z( r, h# ]* H7 b
    {( t' P0 p$ M, {; @
        public byte[] a;
    - T- I4 n+ G4 N6 N* H6 v    public byte[] b;
    * B' Z( p/ a/ V4 p: M# |: ^}0 U8 b7 m, Z. i% m3 I5 ?/ K
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。
    $ D& n# w6 Q1 z, z; }不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。
    ( `3 d6 i; x! S7 Q( ]* b1 \C# 数组的支持
    6 j, i. `% v) l3 w# o6 h数组支持,行为基本和 C# 一致。
    , f* n! ?, B# aByte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    ( C. S3 v8 ^7 L& H: a# c. l对一般数组可以用的设置其中的值的操作$ N( U. G$ q7 ?- J$ ?" c/ b
    short[] some = new short[17];
    2 r$ w+ j; t* e$ ~0 Z: rsome[1] = 12;, W8 E  n3 x8 Z; y8 m) j3 e0 E. z, l
    return some;
    ( C: {, d3 k) E1 ?% u  O对 Byte[] 不允许。( X8 c8 Y5 m1 d8 m% m, x, g
    C# 枚举的支持
    . M# `1 t' f- k6 U; ~支持仅作为数值使用时定义枚举。
    ' [& v& S$ O4 j% z: ^不支持格式化为 String,以及从 String 解析等。
    8 P* X2 c7 i3 [" ~7 B4 R, uC# 容器的支持6 `7 C) C- z! e, T& ?( L3 K2 L+ V
    不支持 C# 常用的 LIST Dictionary 容器。
    1 e4 k* \* d! H' lLIST 功能可以用数组替代。
    ) Q' V3 e; ]# o: t7 e$ |Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    ( a% F" X$ E7 P$ fC# 变量的支持
    / S1 {  E) J7 i, _/ ?; L8 b* y临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。2 j' c, r5 F9 I9 |  l5 `
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
    0 O" r+ a& [1 d6 o' ~- bpublic static BigInteger TotalIcoNeo() => total_neo;
    % C0 C1 q- e2 F; e5 {3 \' iC# 委托和事件的支持
    4 F6 l: X6 v4 l* cC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
    9 R& u4 s  V1 C. I1 i- x* mpublic delegate void acall(string a);
    1 |, q9 R4 a0 m5 L( s. y5 Q一是可以用来定义事件:# n8 f. j- }& c* q. P4 \, {
    public static event acall dododo;
    3 M: K2 j" I; s7 _; s7 N调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。" [+ T% M( a$ l% [- j( x6 H; K$ c
    另一个是可以将一个 bytearray 转型为一个委托:; V$ D5 ?$ Q! e0 ?) @7 W' p+ X
    acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();+ \& K, X& m5 ?7 l6 k1 u
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。
    ! Y$ J* o% M& lC# 开发约定0 f% k1 S; ]" u. b
    C# 的导出要求
    " X4 K  S4 S" ^2 R, }. CNEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。6 n$ P+ I- T$ X$ i* D
    其它要导出的函数都应该为 public static,且不可重名。# ~/ C. z& D) X1 {
    C# 的委托和定义
    4 h8 e6 }+ D9 oC# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。
    $ Y8 p/ f, M8 O9 k, ]# d分别对应 NEO 智能合约的通知与 NEP4。
    ' \9 t9 z5 K8 N# d- t  f内置特性- }9 W4 s) h; p% b7 N( }- l6 ^
    如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    * \, [+ R( l9 A4 j% ], b也可以在你的智能合约代码中使用这些功能。
    9 s) C2 Y, R) L( C$ VAPPCALL特性
    % E# V3 w8 r5 {调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。
    3 o: H. i3 Z# P- _8 L, u+ O5 S[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
    , x/ P6 b' U, A8 Q: zpublic static extern bool AnotherContract(string arg, object[] args);1 h. K0 H7 `% v$ J( y
    SYSCALL特性
    5 D# ?) i$ [  R2 O调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:) i) @2 y. G& t3 e! {& r! P
    [Syscall("Neo.Account.GetBalance")]
      m7 V0 S* A5 a) E* U; upublic extern long GetBalance(byte[] asset_id);  _& L" ^& @5 ~0 K5 o
    OPCALL 特性( s$ q4 o) z& @0 n* T" ~$ H
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:& M3 R% c2 \8 w4 F
    [OpCode(Neo.VM.OpCode.LEFT)]1 s4 p$ a: m# ^) P
    public extern static byte[] Take(byte[] good, int index);
    - A7 z. h! P* g- ?( mNONEMIT 特性8 U: w+ a2 j% n' d' M4 ?1 D
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。: C9 ?. W* n- r4 M5 U; y% N
    [Nonemit]. w3 t. r/ [% i  @$ j7 F. Q9 b; D& Q
    public extern static Delegate ToDelegate(this byte[] source);- ?4 {) q# _; Y2 o
    NonemitWithConvert 特性
    " ~2 ?7 c% s+ i( T0 \. P- m6 a- R! x当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。
    0 F0 m" M# J: B; `3 A: d- F[NonemitWithConvert(ConvertMethod.ToScriptHash)]
    / ~4 N8 O0 j) s; p0 npublic extern static byte[] ToScriptHash(this string address);
    : s- m+ H) |5 a9 Q  r  `! f例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。8 H. x1 |2 N# `7 U5 m
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11