Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Yul 语言说明

一夜雨十年灯潞
631 0 0
Yul (先前被也被称为 JULIA 或 IULIA)是一种可以编译到各种不同后端的中间语言( |evm| 1.0,|evm| 1.5,而 eWASM 也在计划中)。0 G) F- e  c0 w- d
正因为如此,它被设计成为这三种平台的可用的共同标准。! D$ n% d! ]9 f! ?: D3 ~# i, V( {
它已经可以用于 Solidity 内部的“内联汇编”,并且未来版本的 Solidity 编译器甚至会将 Yul 用作中间语言。 为 Yul 构建高级的优化器阶段也将会很容易。
- o) ?, s3 d( M: v! \' p… note::# i- S3 a1 v- z3 Z3 z
请注意,用于“内联汇编”的书写风格是不带类型的(所有的都是 ``u256``),内置函数与 |evm| 操作码相同。
3 A' b  d0 B$ S* I  T& `有关详细信息,请参阅内联汇编文档。
6 }4 z( ^* w  j% o* E  G5 _. ~' ?Yul 的核心组件是函数,代码块,变量,字面量,for 循环,if 条件语句,switch 条件语句,表达式和变量赋值。) [% {9 q4 S/ {5 v1 O' ?
Yul 是强类型的,变量和字面量都需要通过前缀符号来指明类型。支持的类型有:bool, u8, s8, u32, s32,
2 Y5 w" u+ H/ ]* Xu64, s64, u128, s128, u256 和 s256。" c: N. ^% z/ Q, O; k( m' e$ s5 q
Yul 本身甚至不提供操作符。如果目标平台是 |evm|,则操作码将作为内置函数提供,但如果后端平台发生了变化,则可以重新实现它们。
* C- @4 `  b: C有关强制性的内置函数的列表,请参阅下面的章节。% m/ \$ q( R/ P) ~* v  z7 y  p+ e. ?
以下示例程序假定 |evm| 操作码 mul,div 和 mo 是原生支持或可以作为函数用以计算指数的。7 q# N( z5 f9 [" I  q% A
… code::
; E9 ]2 r( [1 ~! y" r3 v{
. G1 }$ m: R6 G    function power(base:u256, exponent:u256) -> result:u2561 U( v3 E2 W" b  C
    {
5 B( J3 P1 x( G        switch exponent2 u! s% ~/ _5 ]7 E2 F
        case 0:u256 { result := 1:u256 }- l( t' c: b6 {
        case 1:u256 { result := base }
' o; J/ T" P4 G0 @0 M1 I        default:, d. s, m7 {+ u" l2 j) L& r
        {# Q/ a" Z% D) N, Z& [9 t
            result := power(mul(base, base), div(exponent, 2:u256))
' ]7 s* F3 h6 t5 [            switch mod(exponent, 2:u256)
/ @) B- C  V  ~& Z+ `                case 1:u256 { result := mul(base, result) }: G# s1 A) [/ z2 Q; V
        }
9 t2 z$ z8 s+ h    }0 ]$ H7 i/ k- i. }) c4 b1 l
}2 O% |9 L/ T: K; a* V& k
也可用 for 循环代替递归来实现相同的功能。这里,我们需要 |evm| 操作码 lt (小于)和 add 可用。& g4 {" _* m+ h4 i% d
… code::
9 d( c. l. Y2 [  O4 e. \5 Y+ G/ u9 z; u{
+ n# o/ `8 V" j( v" l: ~    function power(base:u256, exponent:u256) -> result:u256
" s$ ~/ U/ g( V- j" `6 p6 _; o    {
2 w1 \. m! E. Y0 N8 r        result := 1:u256
8 y1 F+ D+ m2 {" [9 }2 ?        for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) }
3 K# w/ @5 u4 j( R3 u( `        {
" B7 E) K& b4 Q, p9 ~            result := mul(result, base)/ }: @! w; W: X! T/ C7 D
        }2 z& k4 }" G; F0 e5 ?" K- ^; ?5 y, R
    }
/ c0 I2 p8 Z" D/ M6 o3 m5 ?- x}, g& W9 H( }) f( _
Yul 语言说明7 _( c+ A, L2 h4 d( A% e
本章介绍 Yul 代码。Yul 代码通常放置在一个 Yul 对象中,它将在下一节中介绍。
9 p. ]) r; z# r  H1 ~1 k语法::! g% O; d8 X/ d* ^' s8 ]0 i, _/ c
代码块 = '{' 语句* '}'; W$ n1 A7 w( v! m
语句 =
# J* ]  y! Z% a/ o$ q    代码块 |
4 h" m; [7 I5 I- O0 I, t& a+ M    函数定义 |! X5 h1 I% E( z
    变量声明 |
* b3 X; g2 c! z5 {6 y, @) `    赋值 |
! ~- L6 T9 C; `" m5 n    表达式 |
1 q0 i- P- ^3 s, H# @* x1 m: s    Switch |
: z  W# v2 T6 M0 \6 ~    For 循环 |
5 ^  y8 S* C$ u( B    循环中断/ T+ r; S5 ~. y! p# K/ ?
函数定义 =2 c( a% p# b& B: r( d* T
    'function' 标识符 '(' 带类型的标识符列表? ')'0 L* [- z, f# o8 u& m& l
    ( '->' 带类型的标识符列表 )? 代码块. W4 B9 o5 r. `0 J( ^- ?+ d
变量声明 =
% w5 x8 K$ H' `: E    'let' 带类型的标识符列表 ( ':=' 表达式 )?
) R5 H+ r! u+ n* m赋值 =
2 O) }7 O3 ?7 C$ k/ H    标识符列表 ':=' 表达式
; X% s, u4 U- O表达式 =8 r% y1 t1 o5 m3 [0 p- u
    函数调用 | 标识符 | 字面量/ a$ P: X3 ?* _% Y
If 条件语句 =" M& x& l" Z6 U3 Z2 T- |) C
    'if' 表达式 代码块  C' l) ~) T, }& Q
Switch 条件语句 =# M& f1 O' ^  J
    'switch' 表达式 Case* ( 'default' 代码块 )?1 L/ t* N$ W; ~. ^: y( V! K& H
Case =" [/ s) X/ B2 X3 [2 n
    'case' 字面量 代码块
$ G* @. i. G3 r( }& J: nFor 循环 =
2 x" {# _7 T3 k7 X; `    'for' 代码块 表达式 代码块 代码块  h$ v- U6 I" H  y
循环中断 =1 d. ?: n$ \* G# G& z+ Z  A
    'break' | 'continue': ?9 b9 y2 s# |& _/ u- s
函数调用 =
' D* ^, T+ D# R9 D( m- T; ~    标识符 '(' ( 表达式 ( ',' 表达式 )* )? ')'
- |' u! O' C8 B7 }6 G标识符 = [a-zA-Z_$] [a-zA-Z_0-9]*7 F" r( I1 L* B5 g4 v3 |
标识符列表 = 标识符 ( ',' 标识符)*" Q  @2 e9 A4 P& B, b7 Q
类型名 = 标识符 | 内置的类型名1 O$ V- f' _# H+ _# C
内置的类型名 = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )) z8 V$ d- b6 I; G" _0 T( c
带类型的标识符列表 = 标识符 ':' 类型名 ( ',' 标识符 ':' 类型名 )*
: N0 a3 o3 H; M. x字面量 =; z" x: V  _4 a. l: D7 \+ y4 `2 E
    (数字字面量 | 字符串字面量 | 十六进制字面量 | True字面量 | False字面量) ':' 类型名
4 W( p9 }$ T4 w# G) ?数字字面量 = 十六进制数字 | 十进制数字# N1 K& q) W$ H' ]  s
十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
& ^5 Q5 x& c. U! B' z/ z, g字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
  u$ o/ j! ~9 ?5 V7 yTrue字面量 = 'true'
5 C$ p: ?! T% gFalse字面量 = 'false'
8 n- Q: @  S! l8 n- V十六进制数字 = '0x' [0-9a-fA-F]+- I. [; N* H3 ]9 l' C
十进制数字 = [0-9]+
+ q" a0 P/ H0 ~: k4 {/ @语法层面的限制
: \# \  G4 L$ c1 V: P) z" W5 @+ ]Switches 必须至少有一个 case(包括 default )。9 o' G- ^( _. i
如果表达式的所有可能值都被覆盖了,那么不应该允许使用 default
6 p  `$ {$ \. u( B$ a1 b% g5 n(即带 bool 表达式的 switch 语句同时具有 true case 和 false case 的情况下不应再有 default 语句)。
; m- d7 V0 L" ~/ `每个表达式都求值为零个或多个值。 标识符和字面量求值为一个值,函数调用求值为所调用函数的返回值。
: Z; ?" u+ w: u( a在变量声明和赋值中,右侧表达式(如果存在)求值后,必须得出与左侧变量数量相等的值。! n3 z7 ?# G% U* c+ t
这是唯一允许求值出多个值的表达式。! b" k0 s+ ^, S; Q
那种同时又是语句的表达式(即在代码块的层次)求值结果必须只有零个值。4 r  L, G8 V6 E2 |; k$ l5 F
在其他所有情况中,表达式求值后必须仅有一个值。
$ h6 D4 x- e; D1 p  Gcontinue 和 break 语句只能用在循环体中,并且必须与循环处于同一个函数中(或者两者都必须在顶层)。/ x$ u( E. }6 z& e! a9 h
for 循环的条件部分的求值结果只能为一个值。8 S6 D" Q) ^3 D$ i4 R. c8 R* Q
字面量不可以大于它们本身的类型。已定义的最大类型宽度为 256 比特。
! H: T/ f% U. y" V7 M作用域规则3 |/ i  m: J% I; l8 h5 e( {
Yul 中的作用域是与块(除了函数和 for 循环,如下所述)和所有引入新的标识符到作用域中的声明$ p. i/ G% B  e; g" V2 X
( FunctionDefinition ,VariableDeclaration )紧密绑定的。
5 O7 [, i. d7 X" L3 r$ G标识符在将其定义的块中可见(包括所有子节点和子块)。
' N* |) \! z4 S6 G* a1 A# R作为例外,for 循环的 “init” 部分中(第一个块)定义的标识符在 for 循环的所有其他部分(但不在循环之外)中都是可见的。+ o# [) y2 s& l: U$ ^3 B9 v# d
在 for 循环的其他部分声明的标识符遵守常规的作用域语法规则。
# A' m) ]. X/ h# j# x$ P3 Y  C函数的参数和返回参数在函数体中可见,并且它们的名称不能相同。3 p) N8 K' a( [% {( {* K4 G% [0 t
变量只能在声明后引用。 尤其是,变量不能在它们自己的变量声明的右边被引用。
* K: D! Y1 P) g5 l  y/ V( J& o函数可以在声明之前被引用(如果它们是可见的)。
' j# N' `# D5 Y' \8 XShadowing 是不被允许的,即是说,你不能在同名标识符已经可见的情况下又定义该标识符,即使它是不可访问的。
/ D: R9 v& h, Z' z在函数内,不可能访问声明在函数外的变量。$ S. N2 F/ L3 F- d( S
形式规范
3 U3 r" i6 ]. Y% S- }我们通过在 AST 的各个节点上提供重载的求值函数 E 来正式指定 Yul。
6 ]6 x) c. `+ p8 Z任何函数都可能有副作用,所以 E 接受两个状态对象和 AST 节点作为它的参数,并返回两个新的状态对象和数量可变的其他值。, C) @% ]4 N- {6 e& Q$ X/ `2 e
这两个状态对象是全局状态对象(在 |evm| 的上下文中是 |memory|,|storage| 和区块链的状态)和本地状态对象(局部变量的状态,即 |evm| 中堆栈的某个段)。8 r$ i7 [5 g0 J8 ^% U: c. V* Q
如果 AST 节点是一个语句,E 将返回两个状态对象和一个用于 break 和 continue 语句的 “mode”。
* I* [, n9 i. f3 |1 J如果 AST 节点是表达式,则 E 返回两个状态对象,并返回与表达式求值结果相同数量的值。9 W8 U6 U1 |3 {( @( I
在这份高层次的描述中,并没有对全局状态的确切本质进行说明。/ E/ y2 |. r9 B9 @
本地状态 L 是标识符 i 到值 v 的映射,表示为 L = v。' e, X6 b; S4 m" ~3 ?0 b
对于标识符 v, 我们用 $v 作为标识符的名字。0 p" w7 y3 Y# Z0 J5 h- W: @7 [% D
我们将为 AST 节点使用解构符号。7 u" x" C0 l; n" K1 B
… code::
6 H/ u- K+ \' D; \/ |% I2 }; ^E(G, L, : Block) =
9 z# a- F5 ~! ^0 ]( ]; ?& R    let G1, L1, mode = E(G, L, St1, ..., Stn)
! ~9 B4 \! Y" g/ g# y, r2 i/ t    let L2 be a restriction of L1 to the identifiers of L
- W6 `& ?; j/ |7 F# D- ~    G1, L2, mode
8 g2 f5 a* J2 z$ f# d& pE(G, L, St1, ..., Stn: Statement) =9 Q. [# }, [0 o0 c  E/ H4 y
    if n is zero:
1 O% {. j: W( ]        G, L, regular
0 i5 F' W# ~% J& _+ x    else:
3 ?: `) S+ T6 P% ]        let G1, L1, mode = E(G, L, St1)
# P1 c+ m' L* t" h* f; Y5 J        if mode is regular then
1 D& x' O3 D. y1 |. ^            E(G1, L1, St2, ..., Stn)9 Q# R4 m$ A: n/ K( ?( {8 J
        otherwise
( h7 O. ~. G' D, k4 `: I/ p            G1, L1, mode
! J" k2 f7 k+ c' b  {; yE(G, L, FunctionDefinition) =. R0 a' `# x0 A- c1 w. `: r8 X
    G, L, regular
7 i4 G5 G& Z) N4 IE(G, L, : VariableDeclaration) =
- H% p1 W+ K# G. D# l    E(G, L, : Assignment)0 o# O( i& E# X
E(G, L, : VariableDeclaration) =
- p' D" ^! N9 r3 b0 _; o    let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n* y, i3 J& }2 u3 c; F
    G, L1, regular
- Z$ `, @# G8 N5 L3 _; ZE(G, L, : Assignment) =# |$ h* \7 ^4 e) r! }  u
    let G1, L1, v1, ..., vn = E(G, L, rhs)6 _! I) \% ~  u% K& R- A+ |$ K
    let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n
8 M( L$ x  i/ G' \  X    G, L2, regular
) @8 Q5 ?& B; YE(G, L, : ForLoop) =
5 I8 y/ ]: S* U+ i; C3 S    if n >= 1:
4 x! j- i+ S# j& c        let G1, L1, mode = E(G, L, i1, ..., in)
5 {8 X% n# W5 O        // 由于语法限制,mode 必须是规则的* K! N% K; L( h6 b$ ^
        let G2, L2, mode = E(G1, L1, for {} condition post body)6 U( ^, {1 N/ U" M
        // 由于语法限制,mode 必须是规则的
# A0 _' g! K' v" }5 t: ^4 t1 P        let L3 be the restriction of L2 to only variables of L
1 Y# g0 x2 N5 P! h- i) l- @2 r7 x        G2, L3, regular  ~8 _' I( L1 B& \% J3 }
    else:6 I# f' N  n+ e
        let G1, L1, v = E(G, L, condition)! |" i: {3 {* s( S, p- Q
        if v is false:
, E2 k+ N3 h8 {( f/ g            G1, L1, regular
) a8 t+ f) T$ T  J% O        else:
4 k4 R( h1 [( p. i  l& I            let G2, L2, mode = E(G1, L, body)/ o1 \5 O; A3 `  p/ n6 U
            if mode is break:
7 o3 o" j" v9 N5 J. C8 O, T& B* X                G2, L2, regular
, y. F# a4 S7 d5 i) ]! S: q            else:
! S$ L7 f+ Y, b+ }* W+ ?                G3, L3, mode = E(G2, L2, post)  t. M5 C! c/ M( Q7 g. T: z# D
                E(G3, L3, for {} condition post body)* l" h$ ^2 z: Q3 o8 s4 c
E(G, L, break: BreakContinue) =: u" B- T* F7 \
    G, L, break$ x' D) l- X+ r4 Z( w5 }
E(G, L, continue: BreakContinue) =' R, b. m" B& b; P1 ]
    G, L, continue
3 `3 j, |8 n! ]9 UE(G, L, : If) =
) G" i9 ^% B/ z8 g    let G0, L0, v = E(G, L, condition)5 z/ d9 C) j9 G+ S# K: R
    if v is true:6 T$ ^- R8 w7 i8 b/ A6 v( e  ~
        E(G0, L0, body)
+ W* `- q' s* ], n& ]" W/ q& P; b    else:8 q0 I; ~2 }( Q9 \  Q
        G0, L0, regular
& g0 y# T2 b; {+ z8 LE(G, L, : Switch) =
* M2 t4 o( n1 Q, u, e    E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {})
( z& u% C& g6 o9 z. pE(G, L, : Switch) =0 i5 E. j5 \0 b$ {) e( |
    let G0, L0, v = E(G, L, condition)5 b: F: L, |$ }  |- Y
    // i = 1 .. n
2 p; C- m# J: c    // 对字面量求值,上下文无关. j8 T6 Z5 z  i, e# h' f& w
    let _, _, v1 = E(G0, L0, l1), {7 w+ e% T6 U& H
    ...
5 ~- u& \8 ^& `. ]8 c+ {4 o8 x- H    let _, _, vn = E(G0, L0, ln)) a( c* {+ f/ c3 i9 i, k7 Y5 l
    if there exists smallest i such that vi = v:' P/ t% Y; R: n2 P; g- @
        E(G0, L0, sti)
7 P8 U9 Y, i$ s+ \    else:+ B' @) t$ H0 C/ _6 ?9 Z. o
        E(G0, L0, st')
) ^" B$ d# D  j3 a8 C7 T. n2 H4 IE(G, L, : Identifier) =
# G" }, J6 O! x4 E  I7 g6 A+ u    G, L, L[$name]
+ J6 [" h" K6 r4 O/ _$ v2 B3 ZE(G, L, : FunctionCall) =
+ \  e% S0 i9 M) n) @    G1, L1, vn = E(G, L, argn)2 _4 M# v+ Z4 G' B0 e  Y8 n+ L- j
    ...
7 H6 h& N) f, S) \$ s0 v% z; B2 g' Z" }    G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)4 Z( i; o7 l& R6 x+ t
    Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)( h6 u" M8 B0 g0 j
    Let  ret1, ..., retm block>% t: |; \: _7 w; w% U& R' ^
    be the function of name $fname visible at the point of the call.
2 d5 ]6 n( f  A% @* Y3 K2 D    Let L' be a new local state such that8 L% N9 I. ]8 {/ F' w
    L'[$parami] = vi and L'[$reti] = 0 for all i.
2 O. [% N& F: o1 O0 a' o8 o% `    Let G'', L'', mode = E(Gn, L', block)0 q6 s" q$ R  U5 H2 Q
    G'', Ln, L''[$ret1], ..., L''[$retm]! S& X. K/ {7 M  G& J' u
E(G, L, l: HexLiteral) = G, L, hexString(l),
7 L% ^/ y" k% ~3 w: J    where hexString decodes l from hex and left-aligns it into 32 bytes4 s. l% Y. X+ Y
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),6 i/ ^. K8 p5 \  x
    where utf8EncodeLeftAligned performs a utf8 encoding of l3 I- d8 ?2 D' L( \9 F: P* q
    and aligns it left into 32 bytes
1 L) R7 E2 _$ N% I5 u# H8 I4 UE(G, L, n: HexNumber) = G, L, hex(n)
8 Q" ^- I/ v8 t  m! G    where hex is the hexadecimal decoding function1 ]9 x, u/ Q$ i4 Z! O) \
E(G, L, n: DecimalNumber) = G, L, dec(n),. ?3 J* d) Q) `7 m+ ]
    where dec is the decimal decoding function
6 H& I5 F% X2 }" {类型转换函数, N0 Q8 g$ T$ ], o; _: U6 k
Yul 不支持隐式类型转换,因此存在提供显式转换的函数。
  w, d& n' h9 ~5 y( ^2 }在将较大类型转换为较短类型时,如果发生溢出,则可能会发生运行时异常。/ M. l% M' @/ |- ?3 q1 x( h9 k
下列类型的“截取式”转换是允许的:
2 F$ c+ H  s7 h
  • bool
  • u32
  • u64
  • u256
  • s256
    % ^/ J$ `6 t) U. c  ^( l' E* J; ~
    ) \8 Y) W9 Z/ Q( m, W0 S* e9 B1 G
    这里的每种类型的转换函数都有一个格式为 to(x:) -> y: 的原型,
    * @% F7 c6 Q, y+ s* |* K9 J比如 u32tobool(x:u32) -> y:bool、u256tou32(x:u256) -> y:u32 或 s256tou256(x:s256) -> y:u256。) y5 P4 l. ^4 T& ^7 q7 \
    … note::
    ) H$ ^# z/ b. j2 t``u32tobool(x:u32) -> y:bool`` 可以由 ``y := not(iszerou256(x))`` 实现,并且
    # U  J, P6 W% S4 O``booltou32(x:bool) -> y:u32`` 可以由 ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` 实现
    ) W8 ?8 s; s" G( Z. F8 S低级函数
    % t& O& I) A* l5 {9 r, s: ]& c. \) z以下函数必须可用:
    4 K5 f; k+ W6 Q±--------------------------------------------------------------------------------------------------------------+
    ; |8 H! V8 C/ A& G+ a; x9 c! ~| 逻辑操作                                                                                                    |, H$ Q6 ]  Z) m! W$ R
    ±--------------------------------------------±----------------------------------------------------------------+
    0 y2 {* s! ?& W  g/ l| not(x:bool) -> z:bool                       | 逻辑非                                                          |1 \% E* S5 F" J5 R$ t+ ]
    ±--------------------------------------------±----------------------------------------------------------------+7 Z% k$ N/ d* \3 D& D
    | and(x:bool, y:bool) -> z:bool               | 逻辑与                                                          |  y! R( L$ ?9 h: Z; d* F
    ±--------------------------------------------±----------------------------------------------------------------+
    . n5 q+ e) a7 g| or(x:bool, y:bool) -> z:bool                | 逻辑或                                                          |
    , Q" B3 ?5 u. t) {& W0 S. |/ \±--------------------------------------------±----------------------------------------------------------------+% R) k9 S4 U& K: p0 V
    | xor(x:bool, y:bool) -> z:bool               | 异或                                                            |
    4 H6 }* @4 ?. p; k; x/ K/ F$ ?4 I±--------------------------------------------±----------------------------------------------------------------+' _! n$ a* [# {
    | 算术操作                                                                                                    |
    & t- N  I% E8 H5 o5 n1 V±--------------------------------------------±----------------------------------------------------------------+
    ( E+ f! T; A5 ]( l$ t( M) u, G| addu256(x:u256, y:u256) -> z:u256           | x + y                                                           |
    6 T% `( k& P9 ^' x# A& W( A$ X: F±--------------------------------------------±----------------------------------------------------------------+
    ) l  q6 D7 `: h. l| subu256(x:u256, y:u256) -> z:u256           | x - y                                                           |$ @# g3 p' r& x; n
    ±--------------------------------------------±----------------------------------------------------------------+; E7 ^5 a4 B. B, X. K/ b6 b' Q
    | mulu256(x:u256, y:u256) -> z:u256           | x * y                                                           |5 J8 Q( @# `- ^4 [, Z
    ±--------------------------------------------±----------------------------------------------------------------+6 ]$ \9 {1 ^& m  g* w" @
    | divu256(x:u256, y:u256) -> z:u256           | x / y                                                           |
    + G5 ~) x% }2 C* w±--------------------------------------------±----------------------------------------------------------------+5 H2 r7 L: `( \9 Q9 k! a
    | divs256(x:s256, y:s256) -> z:s256           | x / y, 有符号数用补码形式                                       |0 i( A: d  y" D, `
    ±--------------------------------------------±----------------------------------------------------------------+
    $ O2 x5 b5 F4 g; F* h2 Z| modu256(x:u256, y:u256) -> z:u256           | x % y                                                           |- R) O" f& m) ~+ ~
    ±--------------------------------------------±----------------------------------------------------------------+5 y- H0 \$ E! i- T5 f9 w: j
    | mods256(x:s256, y:s256) -> z:s256           | x % y, 有符号数用补码形式                                       |7 G8 H" i) d/ h3 [9 W$ P  z
    ±--------------------------------------------±----------------------------------------------------------------+( s# \- Y3 v8 y9 L1 W% E
    | signextendu256(i:u256, x:u256) -> z:u256    | 从第 (i*8+7) 位开始进行符号扩展,从最低符号位开始计算           |
    8 D4 _# S1 V3 z, p/ w±--------------------------------------------±----------------------------------------------------------------+, [3 T1 b4 ]8 g1 b7 N* G# v% m
    | expu256(x:u256, y:u256) -> z:u256           | x 的 y 次方                                                     |( ~6 j4 P3 `$ v9 t1 U! k; G4 E
    ±--------------------------------------------±----------------------------------------------------------------+* |6 @. [9 Z5 [5 B8 p1 M
    | addmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x + y) % m                                |
    # g0 o8 Y; N4 G- d( f, a& h±--------------------------------------------±----------------------------------------------------------------+
    / B* Z- O, B7 C+ p2 ]9 B| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| 任意精度的数学模运算 (x * y) % m                                |# Z3 A% z- B6 y) {" P& Z
    ±--------------------------------------------±----------------------------------------------------------------+
    5 E4 s% w  O: y1 Q. a| ltu256(x:u256, y:u256) -> z:bool            | 若 x  z:bool            | 若 x > y 为 true, 否则为 false                                  |. g! h$ \# W# [8 P+ y' I1 O# |- o4 O4 s! C
    ±--------------------------------------------±----------------------------------------------------------------+
    ' ]1 _3 A/ j& G" O| sltu256(x:s256, y:s256) -> z:bool           | 若 x  z:bool           | 若 x > y 为 true, 否则为 false                                  |
    1 K& p2 o9 L$ u) I# {( _|                                             | 有符号数用补码形式                                              |* a) z0 G, E( E# N  `
    ±--------------------------------------------±----------------------------------------------------------------+$ R7 \* `- |3 h  W9 A2 s3 N0 t
    | equ256(x:u256, y:u256) -> z:bool            | 若 x == y 为 true, 否则为 false                                 |
    % c2 G7 ^: J" l±--------------------------------------------±----------------------------------------------------------------+
    5 n& t* M/ c: A$ g+ C$ @9 A0 O| iszerou256(x:u256) -> z:bool                | 若 x == 0 为 true, 否则为 false                                 |; u, `) h! p8 Q
    ±--------------------------------------------±----------------------------------------------------------------+
    - M2 X# v1 F& \+ _, d% `| notu256(x:u256) -> z:u256                   | ~x, 对 x 按位非                                                 |9 C# H( q1 z+ o' k) s- F2 p7 _% r
    ±--------------------------------------------±----------------------------------------------------------------+4 [# P( x$ w6 t  v3 u8 s
    | andu256(x:u256, y:u256) -> z:u256           | x 和 y 按位与                                                   |
    9 I7 x( b/ z# s1 r. T9 l±--------------------------------------------±----------------------------------------------------------------+$ h! u) _! |; R( C$ R
    | oru256(x:u256, y:u256) -> z:u256            | x 和 y 按位或                                                   |
    4 X& s( Q8 Q/ _/ F, }±--------------------------------------------±----------------------------------------------------------------+
    2 Z) U+ d! m" w* ]; ]7 E& N| xoru256(x:u256, y:u256) -> z:u256           | x 和 y 按位异或                                                 |+ z$ i! I" r+ I7 n7 d
    ±--------------------------------------------±----------------------------------------------------------------+) s: y/ d: G9 p% V$ M5 b2 F
    | shlu256(x:u256, y:u256) -> z:u256           | 将 x 逻辑左移 y 位                                              |
    7 i) I; B1 D5 W# H1 J4 }±--------------------------------------------±----------------------------------------------------------------+% Y$ u+ ^) f, `& H+ V
    | shru256(x:u256, y:u256) -> z:u256           | 将 x 逻辑右移 y 位                                              |* p" F' a& i8 W% A; W  Q6 e
    ±--------------------------------------------±----------------------------------------------------------------+& q/ O/ e" ^! W2 V% P
    | saru256(x:u256, y:u256) -> z:u256           | 将 x 算术右移 y 位                                              |8 R. G8 R& C9 r/ Y
    ±--------------------------------------------±----------------------------------------------------------------+
    ! q; g+ F, R4 ^* `+ ~' R| byte(n:u256, x:u256) -> v:u256              | x 的第 n 字节,这里的索引位置是从 0 开始的;                    |; N/ c3 e) I% Z
    |                                             | 能否用 and256(shr256(n, x), 0xff) 来替换它,                    |) j/ v4 R- |1 y
    |                                             | 并使它在 EVM 后端之外被优化呢?                                 |2 Y4 F% K8 C# r) U/ G4 V0 b
    ±--------------------------------------------±----------------------------------------------------------------+; ^5 w8 h( i; e- C9 L) N
    | 内存和存储                                                                                                  |
    9 w' l) u; K  e3 C  |. |: U±--------------------------------------------±----------------------------------------------------------------+5 a1 \; P2 A/ d- m
    | mload(p:u256) -> v:u256                     | mem[p…(p+32))                                                  |
    0 _" }# ?9 |3 Z8 m  I  _±--------------------------------------------±----------------------------------------------------------------+
    5 k' b5 W3 v- }' z/ H| mstore(p:u256, v:u256)                      | mem[p…(p+32)) := v                                             |6 `4 x7 g; @$ ?3 e  Z* J$ W
    ±--------------------------------------------±----------------------------------------------------------------+
    ! |) d- q3 d; q3 x/ T| mstore8(p:u256, v:u256)                     | mem := v & 0xff    - 仅修改单个字节                          |2 x/ F+ |3 i; b3 G( k6 d, F* L
    ±--------------------------------------------±----------------------------------------------------------------+4 s0 W; k( ^; `. P# p
    | sload(p:u256) -> v:u256                     | storage                                                      |
    $ [1 D  p- r2 W5 N' c6 g±--------------------------------------------±----------------------------------------------------------------++ L- Y$ u/ {( M( |# c
    | sstore(p:u256, v:u256)                      | storage := v                                                 |" ?2 }! ]( `( W
    ±--------------------------------------------±----------------------------------------------------------------+5 y0 J3 C9 x0 Z1 ~( E( b) i- Y0 o$ Z, Y
    | msize() -> size:u256                        | 内存的大小, 即已访问过的内存的最大下标,                        |
    ' G$ u  U! \4 E4 Z4 x|                                             | 因为内存扩展的限制(只能按字进行扩展)                          |$ a/ z& g6 }+ V3 P
    |                                             | 返回值永远都是 32 字节的倍数                                    |
    8 m8 V  x1 Z( t±--------------------------------------------±----------------------------------------------------------------+
    $ k( S$ b) |* r) R) _| 执行控制                                                                                                    |8 Z+ e# W! {9 s: W4 q+ W5 }
    ±--------------------------------------------±----------------------------------------------------------------+$ K/ t6 z6 F- F" w# [
    | create(v:u256, p:u256, s:u256)              | 以 mem[p…(p+s)) 上的代码创建一个新合约,发送                   |
    % U7 \' s/ o% R  Y/ R|                                             | v 个 wei,并返回一个新的地址                                    |9 Z( X) z7 @6 C" H; [) ^
    ±--------------------------------------------±----------------------------------------------------------------+3 [3 a4 i# y/ R( Q7 _8 o
    | call(g:u256, a:u256, v:u256, in:u256,       | 调用地址 a 上的合约,以 mem[in…(in+insize)) 作为输入           |
    + e/ k  d8 Q5 T1 H8 Q( S) o- D9 @% E9 X% n| insize:u256, out:u256,                      | 一并发送 g gas 和 v wei ,以 mem[out…(out+outsize))            |
    9 k: c, N# L6 p: r# {6 z| outsize:u256)                               | 作为输出空间。若错误,返回 0 (比如,gas 用光                   |6 x( H$ S8 a3 q- C4 a4 T
    | -> r:u256                                   | 成功,返回 1                                                    |8 A$ S: x9 A3 L4 q7 J6 [
    ±--------------------------------------------±----------------------------------------------------------------+
    3 m/ s7 L0 B* d/ g| callcode(g:u256, a:u256, v:u256, in:u256,   | 相当于 call 但仅仅使用地址 a 上的代码,                     |
    + F! K/ M$ E# t  g" j| insize:u256, out:u256,                      | 而留在当前合约的上下文当中                                      |
    : I7 R9 a2 D- O, p5 r| outsize:u256) -> r:u256                     |                                                                 |! k/ K0 e, E; H/ p0 {$ k
    ±--------------------------------------------±----------------------------------------------------------------+! j* ^' W* ?; N0 I
    | delegatecall(g:u256, a:u256, in:u256,       | 相当于 callcode,                                           |
    ! m" ]: i: M( X0 T! J| insize:u256, out:u256,                      | 但同时保留 caller                                           |
    2 |0 V4 ^/ p& V| outsize:u256) -> r:u256                     | 和 callvalue                                                |. E3 q: H* N* B9 _
    ±--------------------------------------------±----------------------------------------------------------------+
    4 c. g- p5 T, t# O' f' q! F| abort()                                     | 终止 (相当于EVM上的非法指令)                                    |4 n% O$ @8 `% l! i6 w
    ±--------------------------------------------±----------------------------------------------------------------+
    % J) j. q4 J8 d) r+ @; K3 h, h| return(p:u256, s:u256)                      | 终止执行,返回 mem[p…(p+s)) 上的数据                           |4 W* |+ |5 s' N% N
    ±--------------------------------------------±----------------------------------------------------------------+
      G% p  {/ m( R+ z2 ~/ q4 D( J| revert(p:u256, s:u256)                      | 终止执行,恢复状态变更,返回 mem[p…(p+s)) 上的数据             |6 u, s/ O4 u/ R" s) o( I3 I8 ]# e
    ±--------------------------------------------±----------------------------------------------------------------++ h" `9 ?9 I- i! q9 b* v/ C7 Q7 P" F
    | selfdestruct(a:u256)                        | 终止执行,销毁当前合约,并且将余额发送到地址 a                  |
    2 m) X7 T1 k; d( N& a- C±--------------------------------------------±----------------------------------------------------------------+; r/ q- Z! f: ]
    | log0(p:u256, s:u256)                        | 用 mem[p…(p+s)] 上的数据产生日志,但没有 topic                 |
    3 o3 X1 e3 @/ M  Z±--------------------------------------------±----------------------------------------------------------------+7 F/ S9 s2 P7 i6 j0 X3 a
    | log1(p:u256, s:u256, t1:u256)               | 用 mem[p…(p+s)] 上的数据和 topic t1 产生日志                   |' R2 [5 B1 s! \9 W. L, C% j) x
    ±--------------------------------------------±----------------------------------------------------------------+
    : `- Y, \- v0 H1 l4 v| log2(p:u256, s:u256, t1:u256, t2:u256)      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2 产生日志               |* T& \# [. g, V) \: N0 v$ F
    ±--------------------------------------------±----------------------------------------------------------------+
    . B: n! r, U0 K: H8 W| log3(p:u256, s:u256, t1:u256, t2:u256,      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3 产生日志           |
    ) ?1 |$ J# R1 n( _8 b6 y; L| t3:u256)                                    |                                                                 |
    3 r6 {  R( t8 g3 j4 n±--------------------------------------------±----------------------------------------------------------------+% U+ i( Q3 c$ H; C1 Q8 G
    | log4(p:u256, s:u256, t1:u256, t2:u256,      | 用 mem[p…(p+s)] 上的数据和 topic t1,t2,t3,t4                |1 G) s9 k4 q' V2 b0 x8 j
    | t3:u256, t4:u256)                           | 产生日志                                                        |
    6 W7 ]/ M0 A1 I* A7 {! }6 v±--------------------------------------------±----------------------------------------------------------------+
    : T( b: _8 [* q* Z, h# D" c8 q| 状态查询                                                                                                    |- w  Y2 [. M7 G% x4 A
    ±--------------------------------------------±----------------------------------------------------------------+) w5 m2 q# M. D& Y* H' n
    | blockcoinbase() -> address:u256             | 当前的矿工                                                      |
    9 o, X0 d& J8 O) l- f±--------------------------------------------±----------------------------------------------------------------+
    - o% r; F% t5 x4 [2 A/ R- h| blockdifficulty() -> difficulty:u256        | 当前区块的难度                                                  |
    ) {1 V* v' i" s±--------------------------------------------±----------------------------------------------------------------+
    ) n+ Z' k7 }; f6 Z1 _% S  e8 O| blockgaslimit() -> limit:u256               | 当前区块的区块 gas 限制                                         |0 G. C' V  S* v
    ±--------------------------------------------±----------------------------------------------------------------+6 t: W; c7 E/ y; f5 O2 p) E
    | blockhash(b:u256) -> hash:u256              | 区块号为 b 的区块的哈希,                                       |
    3 G& j  M8 V! j, n% D' s# Q+ R|                                             | 仅可用于最近的 256 个区块,不包含当前区块                       |
    0 a- V) ]7 ?+ a& W- |+ Y- H±--------------------------------------------±----------------------------------------------------------------+
    ( G- S% u' w) u! Z; _5 A; F: S| blocknumber() -> block:u256                 | 当前区块号                                                      |; S( Z$ i  ~) Q* H; y
    ±--------------------------------------------±----------------------------------------------------------------+/ @7 @8 B5 u. h+ \1 e
    | blocktimestamp() -> timestamp:u256          | 自 epoch 开始的,当前块的时间戳,以秒为单位                     |8 e& P& x' _, O; W6 Q
    ±--------------------------------------------±----------------------------------------------------------------+
    ' J$ o+ S7 f. s3 Q" @0 j| txorigin() -> address:u256                  | 交易的发送方                                                    |1 B  E. x6 w  x! r9 E8 y/ z" O
    ±--------------------------------------------±----------------------------------------------------------------+( h( \9 z+ Q; t: ^# F' h! J9 F! M
    | txgasprice() -> price:u256                  | 交易中的 gas 价格                                               |+ s7 W7 ]  f% [+ u  _$ ?" l9 Y$ x' }- ^
    ±--------------------------------------------±----------------------------------------------------------------++ y% X1 F1 X1 D
    | gasleft() -> gas:u256                       | 还可用于执行的 gas                                              |) ^; g4 X% }7 V9 j( x  d
    ±--------------------------------------------±----------------------------------------------------------------+
    : u) d4 A, i9 b' {| balance(a:u256) -> v:u256                   | 地址 a 上的 wei 余额                                            |" D" }  f0 T+ u" O" _
    ±--------------------------------------------±----------------------------------------------------------------+
    * J! y2 C4 Z- B| this() -> address:u256                      | 当前合约/执行上下文的地址                                      |
    ! W  U% E# B: p4 X/ B±--------------------------------------------±----------------------------------------------------------------+  X7 A/ M3 E3 s4 p: S+ ?4 R' z
    | caller() -> address:u256                    | 调用的发送方 (不包含委托调用)                                   |
    6 U  D, }: t* o4 h+ ^, d: `±--------------------------------------------±----------------------------------------------------------------+
    - f. f' E, L; N$ K9 b% V| callvalue() -> v:u256                       | 与当前调用一起发送的 wei                                        |9 r4 l' E% X0 H5 j
    ±--------------------------------------------±----------------------------------------------------------------+" \) U5 J+ f8 H1 ]( O
    | calldataload(p:u256) -> v:u256              | 从 position p 开始的 calldata (32 字节)                         |' O3 f4 ~. G1 w1 B' c! C
    ±--------------------------------------------±----------------------------------------------------------------+
    % }( P, j, ^8 j/ I| calldatasize() -> v:u256                    | 以字节为单位的 calldata 的大小                                  |+ @5 m6 x( b# c0 \8 s
    ±--------------------------------------------±----------------------------------------------------------------+
    % `% E2 F* N5 q7 e  C% u4 V| calldatacopy(t:u256, f:u256, s:u256)        | 从位置为 f 的 calldata 中,拷贝 s 字节到内存位置 t              |
    7 m* Y( `8 m. [2 X% q' d- l±--------------------------------------------±----------------------------------------------------------------+6 @- a8 z6 J6 @' q: S$ I% y5 @
    | codesize() -> size:u256                     | 当前合约/执行上下文的代码大小                                  |
    $ c  G% n$ g+ w1 S' Q* p! f±--------------------------------------------±----------------------------------------------------------------+
    9 A' A. V5 Z, K' v# i! w, N| codecopy(t:u256, f:u256, s:u256)            | 从 code 位置 f 拷贝 s 字节到内存位置 t                          |# y' P/ R8 F' f; G
    ±--------------------------------------------±----------------------------------------------------------------+  z! g" \, \! D; m" q
    | extcodesize(a:u256) -> size:u256            | 地址 a 上的代码大小                                             |8 s0 O5 d5 O( R
    ±--------------------------------------------±----------------------------------------------------------------+
    ' J0 l  n" |: Z& d+ E$ l| extcodecopy(a:u256, t:u256, f:u256, s:u256) | 相当于 codecopy(t, f, s),但从地址 a 获取代码                   |! j9 x* s" r' W3 M
    ±--------------------------------------------±----------------------------------------------------------------+
    / C: l8 F- T: O. F5 O, V+ M1 x| 其他                                                                                                        |
    ) h6 K% \# c4 p  v. y±--------------------------------------------±----------------------------------------------------------------+
      X" z) u9 d  ]* l1 d, w' p9 s| discard(unused:bool)                        | 丢弃值                                                          |. Q# Q: [6 @! e3 {5 C0 C) @4 V
    ±--------------------------------------------±----------------------------------------------------------------+
    & u5 A3 P& Q- ~| discardu256(unused:u256)                    | 丢弃值                                                          |: J+ |% [$ u3 s0 ~, o8 L$ z9 y
    ±--------------------------------------------±----------------------------------------------------------------+0 s" y0 y* j" F2 A
    | splitu256tou64(x:u256) -> (x1:u64, x2:u64,  | 将一个 u256 拆分为四个 u64                                      |% I* ]5 b  S1 L2 _% R$ Z
    |                            x3:u64, x4:u64)  |                                                                 |, p" N2 M7 `3 B
    ±--------------------------------------------±----------------------------------------------------------------+0 v. J0 |3 D/ N. Q) S
    | combineu64tou256(x1:u64, x2:u64, x3:u64,    | 将四个 u64 组合为一个 u256                                      |8 ]; [% M% N! H; E0 w3 f
    |                  x4:u64) -> (x:u256)        |                                                                 |$ B) L- f0 \, M. H$ |1 N
    ±--------------------------------------------±----------------------------------------------------------------+/ {, _" i8 B& t5 |) q1 J' `( r1 N
    | keccak256(p:u256, s:u256) -> v:u256         | keccak(mem[p…(p+s)))                                          |& f2 a& g3 `! v2 D5 Y* S: k
    ±--------------------------------------------±----------------------------------------------------------------+
    ( ?) s4 x: Q! I- n后端
    % k3 ]: j, O8 u后端或目标负责将 Yul 翻译到特定字节码。 每个后端都可以暴露以后端名称为前缀的函数。 我们为两个建议的后端保留 evm_ 和 ewasm_ 前缀。$ r7 n0 K  y# V, G
    后端: EVM
    0 V* i. }; F! g4 R目标 |evm| 将具有所有用 evm_ 前缀暴露的 |evm| 底层操作码。' a( W- M% J. I6 d+ B& ^+ w' @
    后端: “EVM 1.5”
    . f# G% ?- S; o. t* PTBD
    2 D% f$ {, b. O/ Q$ p2 H! p+ j& \后端: eWASM0 @# i+ X9 O% x! q* l5 e# H- `
    TBD
    , Y! H6 {& i" h/ F- H; dYul 对象说明
    - _9 b8 g9 i+ C! a0 n; J& Z5 k' J( K语法::
    ( p7 o! d5 I5 r! o" A顶层对象 = 'object' '{' 代码? ( 对象 | 数据 )* '}'- ?$ y5 A  I( _/ n4 T
    对象 = 'object' 字符串字面量 '{' 代码? ( 对象 | 数据 )* '}'9 a( \9 a5 d* W2 c
    代码 = 'code' 代码块( |  d# }* s3 f7 g3 j0 \; a
    数据 = 'data' 字符串字面量 十六进制字面量2 ^* j& O8 L* j6 M; E$ x
    十六进制字面量 = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
    & W4 S# X2 ?) ?; L字符串字面量 = '"' ([^"\r\n\\] | '\\' .)* '"'
    ( J1 |9 l# X- `5 \" g6 p9 b1 l在上面,代码块 指的是前一章中解释的 Yul 代码语法中的 代码块。; j. e* j3 |; A0 y1 Y' V# |  C# o
    Yul 对象示例如下:
    7 [& f; i; a2 o- \% X  X" S…code::
    , R* w$ {: e6 V0 b: y// 代码由单个对象组成。 单个 “code” 节点是对象的代码。
    " x: G( C& T. }+ _// 每个(其他)命名的对象或数据部分都被序列化
    ! _6 F. m8 k+ y  J7 ~// 并可供特殊内置函数:datacopy / dataoffset / datasize 用于访问0 w+ l3 ~- [9 D- Y* a+ {
    object {
    6 V6 K6 G/ ^! I1 N: O    code {3 s  q( c$ e1 O: r
            let size = datasize("runtime")
    , V& W- f# ]  H0 @1 A        let offset = allocate(size)
    / E7 H0 d0 R/ D7 x  ?! R' J+ X+ c        // 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy( G8 i: t- l9 R/ c9 c
            datacopy(dataoffset("runtime"), offset, size)
    8 r' |+ `+ Q! f' G- s3 L        // 这是一个构造函数,并且运行时代码会被返回
    0 f3 G* D( y. E4 B/ t        return(offset, size)
    - b# U9 h4 o& f    }# k$ g5 E1 n' H  k  O) G+ u# D
        data "Table2" hex"4123"' H5 U" C2 T* |+ x, J
        object "runtime" {1 n( b  Q" |" Q! F5 \
            code {
    # \2 V( N! E2 n- f% W6 `            // 运行时代码4 P* r" k6 ?( K5 p: u
                let size = datasize("Contract2")
    1 a% g* @2 t1 V  i9 j2 {. b            let offset = allocate(size)
    - D& Y/ Z$ p* Q            // 这里,对于 eWASM 变为一个内存到内存的拷贝,对于 EVM 则相当于 codecopy
    % o- g8 b  M$ x  l            datacopy(dataoffset("Contract2"), offset, size)
    . X: b: S  l: [. A& B0 o/ v            // 构造函数参数是一个数字 0x1234
    8 y8 i/ s, k2 L9 |            mstore(add(offset, size), 0x1234)
    " R1 T% q& u( q4 c& P, \; c            create(offset, add(size, 32))
    7 t* b! R6 C1 W+ k( N. g  U        }
    " n2 \& S* C7 A' _7 z        // 内嵌对象。使用场景是,外层是一个工厂合约,而 Contract2 将是由工厂生成的代码+ S% S5 |# \: r" B" F; g/ c7 L& n
            object "Contract2" {/ [' r, q$ {" H8 e  I8 ~
                code {' C2 ]2 F2 z- Y. r
                    // 代码在这 ...: h7 I) o- |3 @2 u  A
                }+ ^! l/ Q4 \' Q# X/ Z5 w
                object "runtime" {
    9 y& n* k. s8 l+ }8 p4 Z+ w- b$ n                code {8 Q0 d3 y7 J% x
                        // 代码在这 ...% u7 E- m( m9 j  `0 J+ N) {. L7 d2 w
                    }! W+ `, {, Y0 u  Y' m
                 }: s& T7 [8 E% E9 K4 j- I- l8 ~8 q0 c
                 data "Table1" hex"4123"
    ' r7 R: d- W- B        }" {" c: H& b( G2 x) R
        }  e" Y# O# W9 T8 l  z1 U
    }
  • BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
    声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    成为第一个吐槽的人