与zk-SNARK(Succinct Non-interactive ARgument of Knowledge)相比,STARK(Scalable Transparent ARgument of Knowledge)主要有如下不同:
- 1)传统的zk-SNARK依赖于 尖端的密码学假设,而STARK中的密码学元素仅为 collision-resistant hash function。因此,如果选择理想的hash函数,STARK 为抗量子攻击的。第一代使用bilinear map的SANRK,仅在unfalsifiable assumption下provably secure。
- 2)STARK中的field arithmetization独立于cryptographic hard problem,该filed可单独基于性能优化来选择,因此,STARK具有concretely fast prover。
- 3)传统的zk-SNARK需要trusted setup来生成public parameters,在trusted setup中使用的随机数为toxic waste,必须遗弃忘记。拥有这些toxic waste,可伪造proof。相反,STARK无需trusted setup,因此也没有toxic waste。
STARK主要论文有:
- Eli Ben-Sasson等人2017年论文 Fast Reed-Solomon Interactive Oracle Proofs of Proximity
- Eli Ben-Sasson等人2018年论文 Scalable, transparent, and post-quantum secure computational integrity
- Eli Ben-Sasson等人2019年论文 DEEP-FRI: Sampling Outside the Box Improves Soundness
- Eli Ben-Sasson等人2021年论文 Proximity Gaps for Reed–Solomon Codes
STARK示例代码有:
- https://github.com/aszepieniec/stark-anatomy(Python语言):采用Rescue-Prime hash函数。
STARK为interactive proof system,可将STARK看成是特殊类型的SNARK:
- 1)密码学元素仅有hash函数。
- 2)采用AIR(algebraic intermediate representation)来表示arithmetization,将computational integrity claim reduce为certain low degree polynomials。
- 3)采用FRI(Fast Reed-Solomon IOP of Proximity) subprotocol来证明low degree polynomial,FRI本身采用Merkle tree来实例化。
- 4)zero-knowledge是可选项。
STARK的全称为 Scalable Transparent ARgument of Knowledge,其中:
- Scalable有2层含义:
- 1)Prover的running time最多为quasilinear of computation size,不同于SNARK中的Prover允许具有昂贵的complexity。
- 2)Verification time为poly-logarithmic of computation size。
- Transparent:是指所有的verifier messages都是publicly sampled random coins。不需要trusted setup,从而也没有toxic waste。
STARK可分为4个阶段,3次转换:
-
1)第一个阶段为Computation:可将其想象为a program + an input + an output。computation所需的资源可为:time、memory、randomness、secret information、parallelism。根本目的是将computation 转换为某种format,使得资源有限的Verifier可快速验证其计算完整性。
-
2)通过Arithmetization转换,即可进入第2阶段Arithmetic Constraint System:
- Arithmetization转换过程中:对string bit的逻辑和算术运算,转换为,有限域内元素的有限域操作。二种表示方式是等价的。 任何时候,computation state都是存储在a tuple of w w w registers中的,其值来源于有限域 F \mathbb{F} F。定义state transition function: f : F w → F w f:\mathbb{F}^w\rightarrow \mathbb{F}^w f:Fw→Fw,会在每个cycle都更新state。 algebraic execution trace (AET) 为按时间顺序的所有state tuple list。 Arithmetization转换的结果为生成Arithmetic Constraint System。
- Arithmetic Constraint System:为一组等式,其系数和参数源自有限域。当且仅当constraint system具有satisfying solution(即对参数存在唯一解,使得所有等式均成立)时,computation是integral的。 Arithmetic Constraint System中,至少为algebraic execution trace定义了2种类型的constraints:【这2种constraints,又可称为algebraic intermediate representation (AIR)。更高级的STARK可定义更多的constraint类型,使得可在一个cycle内处理memory或register consistency。】
- Boundary constraints:在computation的开始或结束,特定register具有某特定值。
- Transition contraints:任意2个相邻的state tuple,满足state transition function。
-
3)通过Interpolation转换,即可进入 第3阶段Polynomial IOP:
- Interpolation转换:通常插值用于找到穿过a set of data points的多项式。STARK中的插值是指找到能表示arithmetic constraint system的多项式。Interpolation转换的输出结果为an abstract protocol called Polynomial IOP。 STRAK proof system会对algebraic execution trace进行插值——即找到 w w w个多项式 t i ( X ) t_i(X) ti(X),使得domain D D D中的 t i ( X ) t_i(X) ti(X)值对应为algebraic execution trace第 i i i个register的值。这些多项式作为oracle发送给Verifier。 仅当AIR constraints are satisfied时,才会引起send low-degree polynomials to low-degree polynomials的polynomial 操作。Verifier会模拟这些操作,然后派生新的low degree多项式,该新的low degree多项式可证明constraint system satisfiability,从而也可证明computation integrity。换句话说,插值可将 constraint system satisfiability reduce为 a claim about the low degree of certain polynomials。
- Polynomial IOP:通常proof system中Prover将messages发送给Verifier,但是当Verifier not allowed to read这些messages时,可替换为,Prover发送oracle而不是message,oracle为abstract black-box functionalities,Verifier可query其自选的point,该protocol为interactive oracle proof (IOP)。当oracle对应为low degree polynomial时,则为Polynomial IOP。
-
4)通过cryptographic compilation转换,即可进入 第4阶段Cryptographic proof system。
- Cryptographic Compilation with FRI:真实世界中,polynomial oracle并不存在。想要将Polynomial IOP作为中间阶段的协议设计者,必须找到一种方式来commit to a polynomial,然后open该多项式in a point of the verifier’s choosing。FRI为STARK的关键元素,通过使用Merkle tree + Reed-Solomon Codewords来证明多项式的degree boundedness。 多项式 f ( X ) ∈ F [ X ] f(X)\in\mathbb{F}[X] f(X)∈F[X]的Reed-Solomon codeword为 对应特定domain D ⊂ F D\subset \mathbb{F} D⊂F的值。domain D D D的cardinality 要大于 多项式所允许的最大degree。这些Reed-Solomon codeword值可 以merkle tree方式表示,其root即可认为是polynomial commitment。 Fast Reed-Solomon IOP of Proximity (FRI) 中,Prover发送a sequence of Merkle roots corresponding to codewords whose lengths halve in every iteration。Verifier会对相邻rounds中的Merkle tree进行检查来验证简单的线性关系(具体方法为:要求Prover提供特定叶子的authentication path)。 对于诚实的Prover来说,其表示的多项式的degree在每一轮减半,因此远小于codeword的length。而对于malicious Prover,其degree为 codeword length - 1。最后一轮,Prover要发送a non-trivial codeword corresponding to a constant polynomial。
-
Cryptographic proof system:经由 Cryptographic Compilation with FRI ,Polynomial IOP被编译为 an interactive concrete proof system。可通过Fiat-Shamir transform转换为non-interactive proof,从而命名为STARK。
STARK中使用的素数域应满足 p = f ⋅ 2 k + 1 p=f\cdot 2^k+1 p=f⋅2k+1,其中 k k k应足够大。即 存在order 为 2 k 2^k 2k的subgroup。
扩展域: EthSTARK operates over the finite field defined by a 62-bit prime, but the FRI step operates over a quadratic extension field thereof in order to target a higher security level.
3.2 单变量多项式单变量多项式可表示为: f ( X ) = c 0 + c 1 ⋅ X + ⋯ + c d X d = ∑ i = 0 d c i X i f(X)=c_0+c_1\cdot X+\cdots+c_dX^d=\sum_{i=0}^{d}c_iX^i f(X)=c0+c1⋅X+⋯+cdXd=∑i=0dciXi 其中 c i c_i ci为多项式系数, d d d为多项式degree。
STARK中,要求对domain中所有点进行evaluation,而不是对单个点进行evaluation。
Vanishing polynomial: Z D ( X ) = ∏ d ∈ D ( X − d ) Z_D(X)=\prod_{d\in D}(X-d) ZD(X)=∏d∈D(X−d)为首项系数为1的unique lowest-degree polynomial that takes the value 0 0 0 in all points of D D D。
3.3 多变量多项式初始状态 ( a 0 , b 0 ) (a_0,b_0) (a0,b0),每一轮进行如下运算: ( a , b ) → ( a + b 2 , a ⋅ b ) (a,b)\rightarrow (\frac{a+b}{2},\sqrt{a\cdot b}) (a,b)→(2a+b,a⋅b )
令前一轮状态为 ( X 0 , X 1 ) (X_0,X_1) (X0,X1)经由运算后状态为 ( Y 0 , Y 1 ) (Y_0,Y_1) (Y0,Y1),可用多变量多项式表示为: m 0 ( X 0 , X 1 , Y 0 , Y 1 ) = Y 0 − X 0 + X 1 2 m_0(X_0,X_1,Y_0,Y_1)=Y_0-\frac{X_0+X_1}{2} m0(X0,X1,Y0,Y1)=Y0−2X0+X1 m 1 ( X 0 , X 1 , Y 0 , Y 1 ) = Y 1 2 − X 0 ⋅ X 1 m_1(X_0,X_1,Y_0,Y_1)=Y_1^2-X_0\cdot X_1 m1(X0,X1,Y0,Y1)=Y12−X0⋅X1(注意 m 1 ( X 0 , X 1 , Y 0 , Y 1 ) = Y 1 − X 0 ⋅ X 1 m_1(X_0,X_1,Y_0,Y_1)=Y_1-\sqrt{X_0\cdot X_1} m1(X0,X1,Y0,Y1)=Y1−X0⋅X1 不是多项式。)
多变量多项式的表示方式可为:
class MPolynomial:
def __init__( self, dictionary ):
# Multivariate polynomials are represented as dictionaries with exponent vectors
# as keys and coefficients as values. E.g.:
# f(x,y,z) = 17 + 2xy + 42z - 19x^6*y^3*z^12 is represented as:
# {
# (0,0,0) => 17,
# (1,1,0) => 2,
# (0,0,1) => 42,
# (6,3,12) => -19,
# }
self.dictionary = dictionary
3.4 Merkle tree
采用基于FRI的compiler可将Polynomial IOP转换为a concrete proof system。
FRI协议用于确认a committed polynomial具有bounded degree。
FRI全称为Fast Reed-Solomon IOP of Proximity,其中IOP表示interactive oracle proof。
FRI采用codewords来表示,Verifier无法读取Prover发来的所有codewords,Verifier会make oracle-queries to read them in select locations。 FRI中的codewords为Reed-Solomon codewords,即其值对应为the evaluation of some low-degree polynomial in a list of points called the domain
D
D
D。该list的length要大于多项式中可能的非零值系数的个数
ρ
\rho
ρ倍,
ρ
\rho
ρ称为expansion factor(或blowup factor)。 【即有:initial_codeword_length = (degree + 1) * expansion_factor
】
codewords表示low-degree polynomials。codewords采用merkle tree来实例化。
常规的Polynomial commitment scheme为:
- polynomial commitment及实现方式对比
而FRI scheme有所不同。FRI用于证明某codeword属于a polynomial of low degree。所谓low,是指degree 值不高于 ρ ⋅ l e n ( c o d e w o r d ) \rho\cdot len(codeword) ρ⋅len(codeword)。Prover知道codeword具体内容,而Verifier仅知道Merkle root和其选择的leaf。通过authentication path verification来确认the leaf’s membership to the Merkle tree。
4.1 Split-and-Foldproof system中很赞的一个思想是:split-and-fold技术。可:(假设待证明的claim size为 n n n)
- 1)将a claim reduce为 two claims of half the size。
- 2)然后利用Verifier提供的random challenge合并为一个claim。
- 3)重复以上步骤 log 2 ( n ) − 1 \log_2(n)-1 log2(n)−1次,可最终reduce为a trivial size claim,其为true 当且仅当 原始claim为true。
对于FRI,相应的computational claim为:某特定codeword对应为a polynomial of low degree。令 N N N为codeword length。 d d d为该对应多项式的最大degree,表示为 f ( X ) = ∑ i d c i X i f(X)=\sum_{i}^{d}c_iX^i f(X)=∑idciXi。
根据FFT的divide-and-conquer策略,可将多项式分为奇数项和偶数项表示: f ( X ) = f E ( X 2 ) + X ⋅ f O ( X 2 ) f(X)=f_E(X^2)+X\cdot f_O(X^2) f(X)=fE(X2)+X⋅fO(X2) 其中: f E ( X 2 ) = f ( X ) + f ( − X ) 2 = ∑ i = 0 d + 1 2 − 1 c 2 i X 2 i f_E(X^2)=\frac{f(X)+f(-X)}{2}=\sum_{i=0}^{\frac{d+1}{2}-1}c_{2i}X^{2i} fE(X2)=2f(X)+f(−X)=∑i=02d+1−1c2iX2i f O ( X 2 ) = f ( X ) − f ( − X ) 2 X = ∑ i = 0 d + 1 2 − 1 c 2 i + 1 X 2 i f_O(X^2)=\frac{f(X)-f(-X)}{2X}=\sum_{i=0}^{\frac{d+1}{2}-1}c_{2i+1}X^{2i} fO(X2)=2Xf(X)−f(−X)=∑i=02d+1−1c2i+1X2i
FRI协议的关键是根据codeword for f ( X ) f(X) f(X) 来派生 codeword for f ∗ ( X ) = f E ( X ) + α ⋅ f O ( X ) f^{*}(X)=f_E(X)+\alpha\cdot f_O(X) f∗(X)=fE(X)+α⋅fO(X)。其中 α \alpha α为由Verifier提供的random scalar。
假设 D D D为某multiplicative group的subgroup, D D D 具有even order N N N。令 ω \omega ω为生成subgroup D D D的generator: ⟨ ω ⟩ = D ⊂ F p \ { 0 } \langle \omega \rangle = D \subset \mathbb{F}_p \backslash\lbrace 0\rbrace ⟨ω⟩=D⊂Fp\{0}。 令 { f ( ω i ) } i = 0 N − 1 \lbrace f(\omega^i)\rbrace_{i=0}^{N-1} {f(ωi)}i=0N−1 为 f ( X ) f(X) f(X)的codeword,其实对应为evaluation on D D D。
令 D ⋆ = ⟨ ω 2 ⟩ D^\star = \langle \omega^2 \rangle D⋆=⟨ω2⟩ 为另一domain,其length为 D D D的一半。 令 { f E ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f_ E(\omega^{2i})\rbrace_{i=0}^{N/2-1} {fE(ω2i)}i=0N/2−1, { f O ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f_ O(\omega^{2i})\rbrace_{i=0}^{N/2-1} {fO(ω2i)}i=0N/2−1 和 { f ⋆ ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f^\star(\omega^{2i})\rbrace_ {i=0}^{N/2-1} {f⋆(ω2i)}i=0N/2−1 分别为 the codewords for f E ( X ) f_E(X) fE(X), f O ( X ) f_O(X) fO(X) 和 f ⋆ ( X ) f^\star(X) f⋆(X),其实对应为evaluation on D ⋆ D^\star D⋆。
将 f ⋆ ( X ) f^\star(X) f⋆(X) 的定义扩展为: { f ⋆ ( ω 2 i ) } i = 0 N / 2 − 1 = { f E ( ω 2 i ) + α ⋅ f O ( ω 2 i ) } i = 0 N / 2 − 1 . \lbrace f^\star(\omega^{2i})\rbrace_{i=0}^{N/2-1} = \lbrace f_E(\omega^{2i}) + \alpha \cdot f_O(\omega^{2i})\rbrace_{i=0}^{N/2-1} . {f⋆(ω2i)}i=0N/2−1={fE(ω2i)+α⋅fO(ω2i)}i=0N/2−1.
再次根据 f E ( X 2 ) f_E(X^2) fE(X2) 和 f O ( X 2 ) f_O(X^2) fO(X2) 的定义将 f ⋆ ( X ) f^\star(X) f⋆(X) 扩展为: { f ⋆ ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f^\star(\omega^{2i})\rbrace_{i=0}^{N/2-1} {f⋆(ω2i)}i=0N/2−1 = { f ( ω i ) + f ( − ω i ) 2 + α ⋅ f ( ω i ) − f ( − ω i ) 2 ω i } i = 0 N / 2 − 1 = \left\lbrace \frac{f(\omega^i) + f(-\omega^i)}{2} + \alpha \cdot \frac{f(\omega^i) - f(-\omega^i)}{2 \omega^i} \right\rbrace_{i=0}^{N/2-1} ={2f(ωi)+f(−ωi)+α⋅2ωif(ωi)−f(−ωi)}i=0N/2−1 = { 2 − 1 ⋅ ( ( 1 + α ⋅ ω − i ) ⋅ f ( ω i ) + ( 1 − α ⋅ ω − i ) ⋅ f ( − ω i ) ) } i = 0 N / 2 − 1 = \lbrace 2^{-1} \cdot \left( ( 1 + \alpha \cdot \omega^{-i} ) \cdot f(\omega^i) + (1 - \alpha \cdot \omega^{-i} ) \cdot f(-\omega^i) \right) \rbrace_{i=0}^{N/2-1} ={2−1⋅((1+α⋅ω−i)⋅f(ωi)+(1−α⋅ω−i)⋅f(−ωi))}i=0N/2−1
由于 ω \omega ω的order为 N N N,因此有 ω N / 2 = − 1 \omega^{N/2}=-1 ωN/2=−1,从而有 f ( − ω i ) = f ( ω N / 2 + i ) f(-\omega^i)=f(\omega^{N/2+i}) f(−ωi)=f(ωN/2+i)。因此,尽管iterate index减半为由 0 0 0到 N / 2 − 1 N/2-1 N/2−1,但是所有的points并未减半,仍为 { f ( ω i ) } i = 0 N − 1 \{f(\omega^i)\}_{i=0}^{N-1} {f(ωi)}i=0N−1。所有的这些points用于派生 { f ⋆ ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f^\star(\omega^{2i})\rbrace_{i=0}^{N/2-1} {f⋆(ω2i)}i=0N/2−1,尽管派生后的codeword仅有一半length,尽管其polynomial仅有half the degree。
以FRI protocol中的一轮为例:
- 1)Prover commit to f ( X ) f(X) f(X):将其codeword { f ( ω i ) } i = 0 N − 1 \lbrace f(\omega^i)\rbrace_{i=0}^{N-1} {f(ωi)}i=0N−1 的merkle root发送给Verifier。
- 2)Verifier发送challenge α \alpha α。
- 3)Prover计算并commit to f ⋆ ( X ) f^\star(X) f⋆(X):将其codeword { f ⋆ ( ω 2 i ) } i = 0 N / 2 − 1 \lbrace f^\star(\omega^{2i})\rbrace_{i=0}^{N/2-1} {f⋆(ω2i)}i=0N/2−1 的merkle root发送给Verifier。
- 4)Verifier此时有2组polynomial commitments,接下来要验证两者之间的关系是否正确:
f
⋆
(
X
)
=
2
−
1
⋅
(
(
1
+
α
X
−
1
)
⋅
f
(
X
)
+
(
1
−
α
X
−
1
)
⋅
f
(
−
X
)
)
f^\star(X) = 2^{-1} \cdot \left( (1 + \alpha X^{-1}) \cdot f(X) + (1 - \alpha X^{-1} ) \cdot f(-X) \right)
f⋆(X)=2−1⋅((1+αX−1)⋅f(X)+(1−αX−1)⋅f(−X)) 为此,Verifier需随机选择一个index
i
←
R
{
0
,
…
,
N
/
2
−
1
}
i \xleftarrow{R} \lbrace 0, \ldots, N/2-1\rbrace
iR
{0,…,N/2−1},【注意,index的选择是与round关联的。如果index的选择与round不相关,则可能会存在fail to catch hybrid codewords的情况。事实上,可在所有轮中复用相同的index,必要时基于codeword length 进行modulo运算。】 从而可确定3个point:
- A : ( ω i , f ( ω i ) ) A: (\omega^i, f(\omega^i)) A:(ωi,f(ωi))
- B : ( ω N / 2 + i , f ( ω N / 2 + i ) ) B: (\omega^{N/2+i}, f(\omega^{N/2+i})) B:(ωN/2+i,f(ωN/2+i))
- C : ( α , f ⋆ ( ω 2 i ) ) C: (\alpha, f^\star(\omega^{2i})) C:(α,f⋆(ω2i)) 注意, A 和 B A和B A和B的 x x x坐标为 ω 2 i \omega^{2i} ω2i的平方根。
- 5)Prover收到Verifier发来的index i i i之后,仅需提供Merkle authentication paths的 y y y坐标。
- 6)Verifier验证该authentication path与root匹配,同时验证 A , B , C A,B,C A,B,C在同一条直线上(又称为colinearity check)。 colinearity check成立的原因在于:穿过A、B的直线可表示为: y = ∑ i y i ∏ j ≠ i x − x j x i − x j = f ( ω i ) ⋅ x − ω N / 2 + i ω i − ω N / 2 + i + f ( ω N / 2 + i ) ⋅ x − ω i ω N / 2 + i − ω i = f ( ω i ) ⋅ 2 − 1 ⋅ ω − i ⋅ ( x + ω i ) − f ( ω N / 2 + i ) ⋅ 2 − 1 ⋅ ω − i ( x − ω i ) = 2 − 1 ⋅ ( ( 1 + x ⋅ ω − i ) ⋅ f ( ω i ) + ( 1 − x ⋅ ω − i ) ⋅ f ( ω N / 2 + i ) ) . y = \sum_i y_i \prod_{j \neq i} \frac{x - x_j}{x_i - x_j} \\ = f(\omega^i) \cdot \frac{x - \omega^{N/2+i}}{\omega^{i} - \omega^{N/2+i}} + f(\omega^{N/2+i}) \cdot \frac{x - \omega^{i}}{\omega^{N/2+i} - \omega^{i}} \\ = f(\omega^i) \cdot 2^{-1} \cdot \omega^{-i} \cdot (x + \omega^i) - f(\omega^{N/2+i}) \cdot 2^{-1} \cdot \omega^{-i} (x - \omega^i) \\ = 2^{-1} \cdot \left( (1 + x \cdot \omega^{-i}) \cdot f(\omega^i) + (1 - x \cdot \omega^{-i}) \cdot f(\omega^{N/2 + i}) \right) \enspace . y=i∑yij=i∏xi−xjx−xj=f(ωi)⋅ωi−ωN/2+ix−ωN/2+i+f(ωN/2+i)⋅ωN/2+i−ωix−ωi=f(ωi)⋅2−1⋅ω−i⋅(x+ωi)−f(ωN/2+i)⋅2−1⋅ω−i(x−ωi)=2−1⋅((1+x⋅ω−i)⋅f(ωi)+(1−x⋅ω−i)⋅f(ωN/2+i)). 当 x = α x=\alpha x=α时,即有 y = f ⋆ ( ω 2 i ) y= f^\star(\omega^{2i}) y=f⋆(ω2i),从而说明 C C C也在该直线上。
重复以上round即可。一共需要
log
2
(
d
+
1
)
−
1
\log_2(d+1)-1
log2(d+1)−1轮,其中
d
d
d为原始多项式的degree。最终获得的为constant polynomial,其codeword也为constant。最后一轮中,Prover直接将该constant而不是merkle root发送给Verifier即可。
需要多少个colinearity checks才能达到 λ \lambda λ security level?目前暂无定论。
对于code rate ρ \rho ρ,ethSTARK Documentation——Version 1.1中的安全猜想为:
- The hash function used for building Merkle trees needs to have at least 2 λ 2\lambda 2λ output bits.
- The field needs to have at least 2 λ 2^\lambda 2λ elements. (Note that this refers to the field used for FRI. In particular, you can switch to an extension field if the base field is not large enough.)
- You get log 2 ρ − 1 \log_2 \rho^{-1} log2ρ−1 bits of security for every colinearity check, so setting the number of colinearity checks to s = ⌈ λ / log 2 ρ − 1 ⌉ s = \lceil \lambda / \log_2 \rho^{-1} \rceil s=⌈λ/log2ρ−1⌉ achieves λ \lambda λ bits of security.
因此,实际round值为:【要结合num_colinearity_tests来确认。】
def num_rounds( self ):
codeword_length = self.domain_length
num_rounds = 0
while codeword_length > self.expansion_factor and 4*self.num_colinearity_tests
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?