Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Yul 语言说明

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

    本版积分规则

    成为第一个吐槽的人