Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
208 0 0
合约编写须知, z$ w8 a% ?$ Q9 |2 g: i
NEO 编译器支持的 C# 特性1 E6 A- E  ?8 U% x0 b  ?7 t
使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。) R6 e7 `( h( R* r
因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
1 R' ]! s: N0 }9 B# }关于类型
- b- y9 }) O3 h; C: S3 j3 GNeoVM 有几种基本类型:; L2 o% Y: n; R$ x- p' u) \
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface
    ( c# ]& F9 C, e! a" R& V

    ! E/ X: \% n( P而从 AVM 代码中能直接产生的基本类型只有:5 M4 H/ i/ v# D) A% I3 @7 t; ~
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map$ F1 i0 \+ k2 K4 ~8 ^# o6 y

    & h5 _; Z3 q. eC# 的基本类型有! t2 i) [# L# n/ v) B  }& h( U
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String% \9 g  I; a* s8 @' m6 k! Q: P- Y8 I

    4 V- J) k' P6 P; P7 q因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。
    & o4 i$ X' _  ~& t* F4 a7 s3 lC# 整数类型的支持
    & c- C& t4 n" u4 V1 e- |: JInt8 int16 int32 int64 uint8 uint16 uint32 uint64: X: J4 l8 [, ^- F4 E( J3 w/ _
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。
    % }  G/ B. U8 M( _( s! r! T7 o一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。
    $ k5 L- N# G, z另外对 C# 的 BigInteger 也可以支持:
      m9 w1 j  G, G) H9 G% X# Yulong total_neo = 200;, f4 t4 x' A6 I3 D+ ^& H
    BigInteger ico_neo = 300;7 I) j1 d% }$ C% g: B- O, U# J
    BigInteger balance_neo = total_neo - ico_neo;
    " |2 J; A8 q6 q0 ~- U* r/ h1 Z6 H' tulong value = 150;
    % m) Z# R0 J6 J/ P) {需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)& ?5 T* |  p) ^( V* |7 k; S2 v
    对所有整数类型支持数学运算符 + - * / % 加减乘除余:- a1 \1 ]0 P  }, C5 h
    var a1 = abc + 1;3 t: ^; P( U' s
    var a2 = abc - 1;
    , }5 o3 e3 n% S8 b$ Y3 i- `var a3 = abc * 1;
    1 p  X4 i" k8 Y  v( Pvar a4 = abc / 1;( X, U, y9 ]9 z. F/ I( U" q  H0 S
    var a5 = abc % 2;/ [- F9 i5 X' L1 o
    对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    % O2 P! U, @! Y- mif (a1 > a2) ;
    ( i" I& p5 {: ]) z) @if (a2 = a2) ;8 Y2 E/ P0 g* D" y
    if (a1 3 g: L* d7 @! u+ ~
    支持整数的自增操作符:' G0 I1 Q+ q1 b; R
    int k = 100;( ~" @- }6 h0 [4 U# Z9 ~
    for (int j = 0; j , \9 X. |3 l6 Q
    C# 浮点类型的支持' z& C3 l+ `( g, g
    不支持
    & o, k5 A( K- |9 `% G" @+ ~C# bool 类型的支持
    6 l1 X) S" k* e# c基本支持,底层行为和 INT 类似,false 为 int 0。
    ' [# a8 P0 H; v% T+ v  q+ q3 z3 _C# char string 类型的支持0 O8 d) e% [) ^- N0 o' [5 y" S
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。$ E' o- t- I9 o! q" W2 d
    尤其不要使用 string 处理中文。! L% j. r. H7 T' h1 _, v. H8 p
    string ss3 = "ab";
    " N! h/ g0 t; ^: E" o  U; jss3 += "c";
    % I0 I, l/ K# e8 M; r# Z7 S$ k& Xvar ss = "abcdef";
    ! B- h0 b2 y; b6 [# t3 _4 ^var b2 = ss.Length;
    1 ^8 S1 X+ O+ Z- G7 d0 z. l7 {: Svar c = ss + "abc";9 B$ I- Q; J9 j- v8 u1 F
    var d = ss.Substring(1, 2);* e, B& v% U; K
    支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。
    . H+ j' [2 |( Q. l" C另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。
    9 z! B- A: _& D7 schar 类型作为整数支持。3 ~2 h  a8 V. v% E9 P; g
    C# class 和 结构体的支持3 m# M+ ]( n8 ?- C% b
    支持 C# class 和结构体定义。
    0 R3 w8 d: y: D8 fpublic class info1 R- O# e- {) [3 [! N/ b
    {
    8 R) L! Q/ Y) R0 E. D; L    public byte[] a;
      F5 Z9 ~1 Z* W& x& [( L    public byte[] b;
    1 F, W) W* a) o0 _/ Q1 T$ O# ~}. z6 O1 S6 {- d# C
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。. e2 X2 h( d+ S; D
    不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。9 \/ d# A. r& `0 i8 L. b' g
    C# 数组的支持" x7 _" q/ l8 l! W( j
    数组支持,行为基本和 C# 一致。  W  r4 ~* K1 M2 }  S' ~  K
    Byte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。! r8 k+ ]7 m! Q* l  ~
    对一般数组可以用的设置其中的值的操作
    : s7 C2 k5 q1 o; [) ^short[] some = new short[17];
    ; \  ^' _% \8 P. B, L: ^; qsome[1] = 12;6 |  j# e# g1 x* p: H: l8 x
    return some;
    / H" E" m8 i9 c' Q$ |* m% U0 u对 Byte[] 不允许。6 G# u" G4 C9 K) J! k1 |% B; M
    C# 枚举的支持
    0 {( O/ }, G7 e) g6 c+ @支持仅作为数值使用时定义枚举。
    / b4 E: ^" t3 Q/ ^8 B8 J不支持格式化为 String,以及从 String 解析等。1 o2 {  P& g9 H" r6 G4 k
    C# 容器的支持
    2 b* G! ^$ k! Q: I不支持 C# 常用的 LIST Dictionary 容器。) ^7 ^0 ]9 F, D' V8 K( I
    LIST 功能可以用数组替代。/ ~! h8 k5 L2 }! D, g
    Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    * L: E3 Y# ^- b) S# OC# 变量的支持
    - [6 e. U9 H6 x# v% j- ]$ e5 P临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。# A" b# S& K/ g1 K; e  C, @; ^: T7 n
    private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
    + z# E% @; u+ m/ ^2 K$ i% ?. ppublic static BigInteger TotalIcoNeo() => total_neo;4 x* }, w: M* _; ?, i8 d4 z
    C# 委托和事件的支持
    2 c8 M6 `( X! H/ \7 Q+ BC# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
    # x# Y; R" ^  f. I3 n# Ypublic delegate void acall(string a);/ s6 V6 k2 G; {/ {9 y% ?( l
    一是可以用来定义事件:2 D2 q' G4 g- h
    public static event acall dododo;: V  H7 D/ T7 u  c
    调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。6 ~1 Q# d( _7 s  \% \
    另一个是可以将一个 bytearray 转型为一个委托:
    ; i" w, E: h- P7 R7 Macall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();
    / l9 W4 ~$ ]7 S9 A3 e9 j7 J2 O这就实现了对一个指定地址的智能合约的调用,参考 NEP4。, x- \2 V7 R0 \  V, ?3 z' b
    C# 开发约定
    - s: }3 v' X" W1 T& L$ RC# 的导出要求
    9 ~/ }- h5 \! y& X, R. j* CNEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。
    / ]8 K( ?2 ]3 P* u/ M% V9 V其它要导出的函数都应该为 public static,且不可重名。8 R. D0 b; G- Y; x6 T1 u6 [
    C# 的委托和定义% T8 T, G% W, @; i5 y
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。
    ; C' y- p+ y1 k. ?/ s3 o分别对应 NEO 智能合约的通知与 NEP4。
    * G% X5 J0 R! W& C1 l9 ]9 l* E内置特性
    / x7 o6 {! t  G3 d2 w如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。5 w% Z7 h" l! |5 W
    也可以在你的智能合约代码中使用这些功能。
    * W7 D$ S7 ~7 o1 uAPPCALL特性4 Z6 J$ H, |  P0 T9 j! U: Q) r4 S
    调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。
    2 E, K6 W" {; s[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
    $ T6 z. D% }6 u, V) ^- Dpublic static extern bool AnotherContract(string arg, object[] args);# [1 I( p# o( X9 d
    SYSCALL特性
    % W# |) }  v) g. M: q; W- L  E4 }. f调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    ( [4 @# B8 p9 U. y) K! p[Syscall("Neo.Account.GetBalance")]1 v8 P7 h. x: N/ q  u7 s) Z
    public extern long GetBalance(byte[] asset_id);
    % O8 U( Z: A0 c/ g2 }/ u, P/ nOPCALL 特性( O4 l  E" o6 i0 @# x
    调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:
    0 W4 U, s( {0 Y- c- K9 E* k[OpCode(Neo.VM.OpCode.LEFT)]" K9 W+ k0 _; V- P8 f
    public extern static byte[] Take(byte[] good, int index);
    # s0 s% _8 v7 ~. I; N' g% e! g: R; qNONEMIT 特性) h5 l/ s" ?) @
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。. Z. M- e3 }8 W- C; M
    [Nonemit]& X; _% c' c/ d$ W( P8 a
    public extern static Delegate ToDelegate(this byte[] source);
    8 x$ F5 h  }- Y& ^$ |. Z' \NonemitWithConvert 特性
    5 z4 K3 N9 y8 l/ A& w3 \当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。  B  a9 j# G* k+ ]; a! I
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]# V2 i# K4 c8 L2 r# X9 C; o
    public extern static byte[] ToScriptHash(this string address);, N0 a' g0 H6 Q9 w' d9 V
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。3 a8 w+ u5 \6 x5 w- H
    而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11