Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

NEO合约编写须知

哈哈笑417
87 0 0
合约编写须知8 X$ c5 Q- b) C' B5 L
NEO 编译器支持的 C# 特性. H1 v4 h+ l2 u+ s/ m& l) v
使用 C# 开发智能合约时,你无法使用 C# 的全部特性。会存在一些限制以及不支持的功能,这些限制的根源,来自于 NeoVM 和 Dotnet IL 的差异性。
% U7 l6 T: ?5 z因为 NeoVM 更加精简,所以我们只能将有限的 C# / dotnet 特性编译为 AVM 文件。
* Z; k" T5 J& Y. K+ y# C关于类型1 w9 e/ @. `* G% n
NeoVM 有几种基本类型:  W: I) s: y8 J% n$ X7 D# [6 s
  • ByteArray
  • Integer
  • Boolean
  • Array
  • Struct
  • Map
  • Interface0 O' f1 R* K0 V2 |6 W  \0 n- E& X

    0 Q& Y7 j* m# E. I4 |( ~而从 AVM 代码中能直接产生的基本类型只有:- K* k, h* E3 b9 l. e, ]7 u
  • ByteArray(Integer 和 Boolean 均由 ByteArray 表示)
  • Array
  • Struct
  • Map6 K& d' G8 y6 Q. P9 [
    0 F. g4 Q* X7 T# l6 i
    C# 的基本类型有
    # g: ?8 o! m6 z* D7 r
  • Int8 int16 int32 int64 uint8 uint16 uint32 uint64
  • float double
  • Boolean
  • Char String5 B1 X* U8 E% }3 u4 s& @0 V% R) G2 S

    5 w9 E9 ~, p7 o& d! D' g因为虚拟机层次基本类型的差异,使得 C# 基本类型并不能完全支持,使用中会有一些特殊情况。7 m5 R) c; w5 ]! M
    C# 整数类型的支持
    ! y0 S, Y; ?, [3 E) n3 DInt8 int16 int32 int64 uint8 uint16 uint32 uint64) }; x! Z  p8 R4 s  J0 ~% l: I' d
    因为 NeoVM 只有一种 Integer,底层实现是 BigInteger,比 C# 的范围要大,所以这些整数类型均可使用。8 K( g0 B$ F7 [  N# f6 m( Y) T
    一种数值类型 VARINT,在底层实现时表现为 BigInteger 类型。9 [$ h$ A+ ], ?
    另外对 C# 的 BigInteger 也可以支持:: {/ Y4 Y8 {; y. f& z' t5 ~3 J
    ulong total_neo = 200;& d" I' U# t- ~' T
    BigInteger ico_neo = 300;8 f% V) Y4 B* O: i* I) N9 v! j5 V) v
    BigInteger balance_neo = total_neo - ico_neo;( s0 K. J! O  ]! U# E" ?- ?4 d
    ulong value = 150;
    ( n" x0 ~1 t& H+ q3 r需要注意在将数值类型转型为更小的类型时,编译为 AVM 之后并不会截断数值(byte)(ulong)
    6 F/ M4 i% o# J2 n对所有整数类型支持数学运算符 + - * / % 加减乘除余:
    . J: `8 R' |7 f3 b9 u$ |% P" H. zvar a1 = abc + 1;+ C  {3 M+ `* G' s* S6 K5 {
    var a2 = abc - 1;
    1 q3 h$ A* a* @- _var a3 = abc * 1;. }6 ^+ N) q$ k. `5 E
    var a4 = abc / 1;
    5 X% D) ~5 E* s7 h! P0 @+ G. ?var a5 = abc % 2;2 Z) c4 ^' |2 P% ~0 y9 c  V7 W
    对所有整数类型支持逻辑运算 大于、大于等于、小于、小于等于、等于、不等于:
    5 \' b/ N. J$ u' i- ^if (a1 > a2) ;
    1 t9 t/ I! d/ v. |. u2 p( m# X% Dif (a2 = a2) ;8 E: B! Z2 X! h0 U! v
    if (a1 " ?" U/ W. |, P) u- e! m3 A# @, ]
    支持整数的自增操作符:' i+ W& F$ G# h" n% A" \; E
    int k = 100;  S$ w5 j9 B: M
    for (int j = 0; j
    , \- J: x* T+ U' |1 o  RC# 浮点类型的支持
    0 A" a4 c) Q$ K0 I不支持/ |; r# @$ l; k9 p
    C# bool 类型的支持
    6 q) c5 e' `& T# l  m" j. d基本支持,底层行为和 INT 类似,false 为 int 0。
    5 O# \) x- F/ @C# char string 类型的支持  ]" F5 B) O) k! ~4 u
    不完全支持,由于在 NeoVM 层次,string 也是作为 bytearray 处理,和 C# 中的 string 是不同的,编译到 AVM 的 string 实际是它的 UTF8 编码的 bytearray,请勿使用任何 string 高级处理函数,仅将 string 作为一种特殊类型处理。+ \, o4 Y5 t+ N# A4 s& U7 c# @
    尤其不要使用 string 处理中文。
    ' X( `& p3 e/ A/ t8 xstring ss3 = "ab";% k# R9 Y6 h# ]' D5 U
    ss3 += "c";0 U8 K: {5 g5 l# W- Z  ]4 a! R/ y3 z+ g
    var ss = "abcdef";
    9 H$ Z# \6 u" U3 a' m; gvar b2 = ss.Length;0 l7 C2 G  x3 r/ @* z3 H  s$ Q1 F/ [
    var c = ss + "abc";
    0 S7 S. u; G7 U3 Q; yvar d = ss.Substring(1, 2);$ U- D: B2 W6 g: Z& `6 E, f! p5 W
    支持针对 bytes 操作的 string 的连接,取长度,截取操作,在英文处理时与 C# string 相同,不支持处理中文。1 v5 H" q4 x! r( G; z" G
    另外,由于没有其它类型格式化为 string 的支持,所以 “abc”+1.ToString() 与 C# 结果不同。. r& ~: [: `( y8 ^" \
    char 类型作为整数支持。! z6 U% i1 m' s, j1 i& u( V
    C# class 和 结构体的支持& \9 K- Y+ a+ v; ]# j; g4 e
    支持 C# class 和结构体定义。
    ' u3 p& X" S. x7 W5 fpublic class info
      V: o/ t3 y0 L{+ D% ~" K) [- |8 G0 Z' i5 `4 s
        public byte[] a;
    ! U, J7 e3 R. i1 C8 @8 ^+ z: G    public byte[] b;. `8 V1 r9 P2 x
    }5 \( {, M6 }- w1 b/ b
    不支持在其中自定义成员函数,使用 APPCALL 之类特性的 extern 成员函数例外。; q/ L$ }" [/ r- c$ ^! N1 e. o$ [
    不支持自定义构造函数,使用 OPCALL 特性的 extern 构造函数例外。
    ; o3 |4 |; }6 dC# 数组的支持
    , X3 h" N% h( o% B数组支持,行为基本和 C# 一致。
    ; r# u- l" U# I3 D1 ?7 MByte[] 例外,因为 byte[] 是 NeoVM 底层的特别类型。
    ) p7 _$ e' m9 d5 P8 E对一般数组可以用的设置其中的值的操作4 S$ I5 ]+ e1 y+ H+ {3 F! |8 I
    short[] some = new short[17];. Q" g1 l- E2 _2 A
    some[1] = 12;* B" F; Q/ d- F# E
    return some;( k6 P( r$ V% J- K  E8 X0 U
    对 Byte[] 不允许。
    ; V+ a4 A" B9 i$ `& O) g2 FC# 枚举的支持
    ; ?9 s$ r/ N# J# O; `支持仅作为数值使用时定义枚举。
    ; v! b( ]. ?+ I4 b# s不支持格式化为 String,以及从 String 解析等。* X) f: k) }( A, f: w
    C# 容器的支持
    . Y/ F4 }1 M( r9 R不支持 C# 常用的 LIST Dictionary 容器。
    * A  @- _% l5 z8 bLIST 功能可以用数组替代。' E! x- |8 H+ G" N
    Dictionary 功能可以用 NEO DOTNET DEVPACK 中的 MAP 替代。
    * `- V, b( X! F7 c/ `C# 变量的支持
    / j/ [9 v2 a3 q% X临时变量不限,支持定义 const 变量和静态成员变量。支持静态成员变量直接赋初值。
    4 u- Q+ c3 A, p5 T( w' O/ |private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
    : Y! l* J% r2 gpublic static BigInteger TotalIcoNeo() => total_neo;; m1 L/ S- M" O/ j  v1 U
    C# 委托和事件的支持1 j) o4 J/ r" r* ]
    C# 委托可以定义,定义的委托有两个功能,都是 NeoVM 的特别功能。
    0 T6 c- u* i- f& `public delegate void acall(string a);/ X9 a) b5 t6 G
    一是可以用来定义事件:
    . O& M% s- n& N- K# f/ |public static event acall dododo;6 b# Q6 g. }5 U" B7 y
    调用这个事件则会被 NEO C# 编译器理解为调用 Notify 方法,可参考 NEP5 的通知事件。- ~* k, a5 x- I# O, k2 _
    另一个是可以将一个 bytearray 转型为一个委托:
    * u. [3 M, A* V0 x2 [acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();' p3 v9 I: a7 @0 V& `8 [
    这就实现了对一个指定地址的智能合约的调用,参考 NEP4。
    + p6 h. D) _! U% }# N; S1 f$ R6 h( FC# 开发约定# b. G+ j9 h7 ?- w7 q! l' y
    C# 的导出要求( z1 \$ B* B3 {- s$ Y0 _
    NEO C# 编译器要求一个智能合约有且只有一个名为 Main 的 public static 函数作为入口点。( j4 R0 ]1 v- }: I
    其它要导出的函数都应该为 public static,且不可重名。* L3 |7 `) s& D0 k2 S4 e
    C# 的委托和定义% w* T  m! G4 T4 W4 M
    C# 的委托和事件具有特殊的功能,参考 C# 委托和事件的支持。3 t# g. b% X$ D3 T- Y) X+ B: ~; Z
    分别对应 NEO 智能合约的通知与 NEP4。
    2 b; T4 Y, y: `9 i6 Z( o/ X0 P( u/ Q5 y内置特性
    : E/ ?6 w' G9 f. G$ m( `8 G2 F如果你观察 NEO DEVPACK 会看到很多 extern 的外部函数,实际上他们并没有外部实现,只是不需要实现。他们由特性标记功能。
    9 X" m& H4 ]8 [也可以在你的智能合约代码中使用这些功能。
    - c& ?6 I" z" n8 Z' ZAPPCALL特性
    ; }( k/ A8 g- p2 \5 h调用一个具有 APPCALL 特性的函数,会调用指定的智能合约。
    ; b8 ^/ ]$ u2 v$ T) e[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
    ' p' ^; K2 h+ g. }public static extern bool AnotherContract(string arg, object[] args);
    1 s/ ~6 ~4 x! s# t& t. t5 SSYSCALL特性
      `' p8 m$ K; n8 ]调用一个具有 Syscall 特性的函数,实际上会调用对应的系统函数:
    1 m3 B; g9 M. M6 X% w: u[Syscall("Neo.Account.GetBalance")]: B8 ^( \, o: p+ @
    public extern long GetBalance(byte[] asset_id);4 _) I# d, |1 B: f
    OPCALL 特性
    * }% W6 K3 ]' D调用一个具有 OPCODE 特性的函数时,该调用会被翻译成一条指令:' N) ~" v. v+ {# r
    [OpCode(Neo.VM.OpCode.LEFT)]
    1 K0 ]( P4 }+ Upublic extern static byte[] Take(byte[] good, int index);/ w4 V8 C* ]+ `& s% r+ S
    NONEMIT 特性- ~6 L; L0 v* l! h, B+ K, u$ ~
    执行一个具有 NonEMit 特性的函数,通常都是用来完成一些满足语法的转换,实际上在 NeoVM 底层并不需要转换。
    1 m  R! l- g3 Q7 ]" d[Nonemit]7 D/ H0 a! S8 T# n2 Q
    public extern static Delegate ToDelegate(this byte[] source);
    . V: ]) Y/ k# M4 Y, ?1 F5 XNonemitWithConvert 特性
    " g: m9 V6 [, T/ \当执行一个具有 NonemitWithConvert 特性的函数时,实际只是执行一个转换。这个函数的入参必须是一个常数,因为这个转换是在编译阶段执行的。. Y; i1 t, h7 v/ r; [
    [NonemitWithConvert(ConvertMethod.ToScriptHash)]6 ?8 q6 A2 J& v
    public extern static byte[] ToScriptHash(this string address);' T( c; E5 M/ }
    例如, "ASH……wk".ToScriptHash(); 是合法的,因为编译器可以执行对 “ABCD” 的转换。
    ; y- K8 V6 e7 Y9 a& i而 String xxx = "ASH……wk"; xxx.ToScriptHash(); 是非法的,因为编译器无法确定 XXX 的值。
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人

    哈哈笑417 初中生
    • 粉丝

      0

    • 关注

      0

    • 主题

      11